summaryrefslogtreecommitdiff
path: root/lib/chef
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef')
-rw-r--r--lib/chef/action_collection.rb32
-rw-r--r--lib/chef/application.rb2
-rw-r--r--lib/chef/application/base.rb17
-rw-r--r--lib/chef/application/knife.rb234
-rw-r--r--lib/chef/application/windows_service.rb338
-rw-r--r--lib/chef/application/windows_service_manager.rb205
-rw-r--r--lib/chef/applications.rb1
-rw-r--r--lib/chef/chef_fs/command_line.rb7
-rw-r--r--lib/chef/chef_fs/file_pattern.rb4
-rw-r--r--lib/chef/chef_fs/file_system.rb19
-rw-r--r--lib/chef/chef_fs/knife.rb160
-rw-r--r--lib/chef/chef_fs/parallelizer.rb102
-rw-r--r--lib/chef/chef_fs/parallelizer/flatten_enumerable.rb35
-rw-r--r--lib/chef/chef_fs/parallelizer/parallel_enumerable.rb278
-rw-r--r--lib/chef/client.rb10
-rw-r--r--lib/chef/compliance/default_attributes.rb21
-rw-r--r--lib/chef/compliance/fetcher/automate.rb30
-rw-r--r--lib/chef/compliance/fetcher/chef_server.rb4
-rw-r--r--lib/chef/compliance/reporter/automate.rb25
-rw-r--r--lib/chef/compliance/reporter/chef_server_automate.rb21
-rw-r--r--lib/chef/compliance/reporter/cli.rb77
-rw-r--r--lib/chef/compliance/reporter/compliance_enforcer.rb4
-rw-r--r--lib/chef/compliance/reporter/json_file.rb9
-rw-r--r--lib/chef/compliance/runner.rb177
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb6
-rw-r--r--lib/chef/cookbook/gem_installer.rb6
-rw-r--r--lib/chef/cookbook/synchronizer.rb8
-rw-r--r--lib/chef/cookbook_loader.rb6
-rw-r--r--lib/chef/cookbook_site_streaming_uploader.rb244
-rw-r--r--lib/chef/cookbook_uploader.rb1
-rw-r--r--lib/chef/cookbook_version.rb30
-rw-r--r--lib/chef/data_bag.rb3
-rw-r--r--lib/chef/data_bag_item.rb16
-rw-r--r--lib/chef/data_collector.rb1
-rw-r--r--lib/chef/data_collector/run_end_message.rb2
-rw-r--r--lib/chef/delayed_evaluator.rb4
-rw-r--r--lib/chef/deprecated.rb8
-rw-r--r--lib/chef/dsl/chef_vault.rb12
-rw-r--r--lib/chef/dsl/declare_resource.rb15
-rw-r--r--lib/chef/dsl/reboot_pending.rb5
-rw-r--r--lib/chef/encrypted_data_bag_item/assertions.rb2
-rw-r--r--lib/chef/event_dispatch/base.rb3
-rw-r--r--lib/chef/exceptions.rb3
-rw-r--r--lib/chef/file_access_control/windows.rb8
-rw-r--r--lib/chef/file_cache.rb8
-rw-r--r--lib/chef/formatters/doc.rb3
-rw-r--r--lib/chef/formatters/error_inspectors/resource_failure_inspector.rb42
-rw-r--r--lib/chef/group.rb75
-rw-r--r--lib/chef/handler.rb54
-rw-r--r--lib/chef/handler/json_file.rb2
-rw-r--r--lib/chef/handler/slow_report.rb66
-rw-r--r--lib/chef/http/ssl_policies.rb41
-rw-r--r--lib/chef/knife.rb665
-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.rb1142
-rw-r--r--lib/chef/knife/bootstrap/chef_vault_handler.rb162
-rw-r--r--lib/chef/knife/bootstrap/client_builder.rb212
-rw-r--r--lib/chef/knife/bootstrap/templates/README.md11
-rw-r--r--lib/chef/knife/bootstrap/templates/chef-full.erb242
-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.rb104
-rw-r--r--lib/chef/knife/client_create.rb101
-rw-r--r--lib/chef/knife/client_delete.rb62
-rw-r--r--lib/chef/knife/client_edit.rb52
-rw-r--r--lib/chef/knife/client_key_create.rb73
-rw-r--r--lib/chef/knife/client_key_delete.rb80
-rw-r--r--lib/chef/knife/client_key_edit.rb83
-rw-r--r--lib/chef/knife/client_key_list.rb73
-rw-r--r--lib/chef/knife/client_key_show.rb80
-rw-r--r--lib/chef/knife/client_list.rb41
-rw-r--r--lib/chef/knife/client_reregister.rb58
-rw-r--r--lib/chef/knife/client_show.rb48
-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.rb150
-rw-r--r--lib/chef/knife/configure_client.rb48
-rw-r--r--lib/chef/knife/cookbook_bulk_delete.rb71
-rw-r--r--lib/chef/knife/cookbook_delete.rb151
-rw-r--r--lib/chef/knife/cookbook_download.rb142
-rw-r--r--lib/chef/knife/cookbook_list.rb47
-rw-r--r--lib/chef/knife/cookbook_metadata.rb106
-rw-r--r--lib/chef/knife/cookbook_metadata_from_file.rb49
-rw-r--r--lib/chef/knife/cookbook_show.rb98
-rw-r--r--lib/chef/knife/cookbook_upload.rb292
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb264
-rw-r--r--lib/chef/knife/core/cookbook_scm_repo.rb159
-rw-r--r--lib/chef/knife/core/gem_glob_loader.rb138
-rw-r--r--lib/chef/knife/core/generic_presenter.rb232
-rw-r--r--lib/chef/knife/core/hashed_command_loader.rb100
-rw-r--r--lib/chef/knife/core/node_editor.rb130
-rw-r--r--lib/chef/knife/core/node_presenter.rb158
-rw-r--r--lib/chef/knife/core/object_loader.rb115
-rw-r--r--lib/chef/knife/core/status_presenter.rb172
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb203
-rw-r--r--lib/chef/knife/core/text_formatter.rb85
-rw-r--r--lib/chef/knife/core/ui.rb338
-rw-r--r--lib/chef/knife/core/windows_bootstrap_context.rb406
-rw-r--r--lib/chef/knife/data_bag_create.rb81
-rw-r--r--lib/chef/knife/data_bag_delete.rb49
-rw-r--r--lib/chef/knife/data_bag_edit.rb74
-rw-r--r--lib/chef/knife/data_bag_from_file.rb113
-rw-r--r--lib/chef/knife/data_bag_list.rb42
-rw-r--r--lib/chef/knife/data_bag_secret_options.rb122
-rw-r--r--lib/chef/knife/data_bag_show.rb69
-rw-r--r--lib/chef/knife/delete.rb125
-rw-r--r--lib/chef/knife/deps.rb156
-rw-r--r--lib/chef/knife/diff.rb83
-rw-r--r--lib/chef/knife/download.rb84
-rw-r--r--lib/chef/knife/edit.rb88
-rw-r--r--lib/chef/knife/environment_compare.rb128
-rw-r--r--lib/chef/knife/environment_create.rb52
-rw-r--r--lib/chef/knife/environment_delete.rb44
-rw-r--r--lib/chef/knife/environment_edit.rb44
-rw-r--r--lib/chef/knife/environment_from_file.rb84
-rw-r--r--lib/chef/knife/environment_list.rb41
-rw-r--r--lib/chef/knife/environment_show.rb47
-rw-r--r--lib/chef/knife/exec.rb99
-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/key_create.rb112
-rw-r--r--lib/chef/knife/key_create_base.rb50
-rw-r--r--lib/chef/knife/key_delete.rb55
-rw-r--r--lib/chef/knife/key_edit.rb118
-rw-r--r--lib/chef/knife/key_edit_base.rb55
-rw-r--r--lib/chef/knife/key_list.rb90
-rw-r--r--lib/chef/knife/key_list_base.rb45
-rw-r--r--lib/chef/knife/key_show.rb53
-rw-r--r--lib/chef/knife/list.rb177
-rw-r--r--lib/chef/knife/node_bulk_delete.rb75
-rw-r--r--lib/chef/knife/node_create.rb47
-rw-r--r--lib/chef/knife/node_delete.rb46
-rw-r--r--lib/chef/knife/node_edit.rb70
-rw-r--r--lib/chef/knife/node_environment_set.rb53
-rw-r--r--lib/chef/knife/node_from_file.rb51
-rw-r--r--lib/chef/knife/node_list.rb44
-rw-r--r--lib/chef/knife/node_policy_set.rb79
-rw-r--r--lib/chef/knife/node_run_list_add.rb104
-rw-r--r--lib/chef/knife/node_run_list_remove.rb67
-rw-r--r--lib/chef/knife/node_run_list_set.rb66
-rw-r--r--lib/chef/knife/node_show.rb62
-rw-r--r--lib/chef/knife/null.rb12
-rw-r--r--lib/chef/knife/raw.rb123
-rw-r--r--lib/chef/knife/recipe_list.rb32
-rw-r--r--lib/chef/knife/rehash.rb50
-rw-r--r--lib/chef/knife/role_bulk_delete.rb66
-rw-r--r--lib/chef/knife/role_create.rb53
-rw-r--r--lib/chef/knife/role_delete.rb46
-rw-r--r--lib/chef/knife/role_edit.rb45
-rw-r--r--lib/chef/knife/role_env_run_list_add.rb87
-rw-r--r--lib/chef/knife/role_env_run_list_clear.rb55
-rw-r--r--lib/chef/knife/role_env_run_list_remove.rb57
-rw-r--r--lib/chef/knife/role_env_run_list_replace.rb60
-rw-r--r--lib/chef/knife/role_env_run_list_set.rb70
-rw-r--r--lib/chef/knife/role_from_file.rb51
-rw-r--r--lib/chef/knife/role_list.rb42
-rw-r--r--lib/chef/knife/role_run_list_add.rb87
-rw-r--r--lib/chef/knife/role_run_list_clear.rb55
-rw-r--r--lib/chef/knife/role_run_list_remove.rb56
-rw-r--r--lib/chef/knife/role_run_list_replace.rb60
-rw-r--r--lib/chef/knife/role_run_list_set.rb69
-rw-r--r--lib/chef/knife/role_show.rb48
-rw-r--r--lib/chef/knife/search.rb193
-rw-r--r--lib/chef/knife/serve.rb65
-rw-r--r--lib/chef/knife/show.rb72
-rw-r--r--lib/chef/knife/ssh.rb643
-rw-r--r--lib/chef/knife/ssl_check.rb284
-rw-r--r--lib/chef/knife/ssl_fetch.rb161
-rw-r--r--lib/chef/knife/status.rb98
-rw-r--r--lib/chef/knife/supermarket_download.rb121
-rw-r--r--lib/chef/knife/supermarket_install.rb192
-rw-r--r--lib/chef/knife/supermarket_list.rb76
-rw-r--r--lib/chef/knife/supermarket_search.rb53
-rw-r--r--lib/chef/knife/supermarket_share.rb166
-rw-r--r--lib/chef/knife/supermarket_show.rb66
-rw-r--r--lib/chef/knife/supermarket_unshare.rb61
-rw-r--r--lib/chef/knife/tag_create.rb52
-rw-r--r--lib/chef/knife/tag_delete.rb60
-rw-r--r--lib/chef/knife/tag_list.rb47
-rw-r--r--lib/chef/knife/upload.rb86
-rw-r--r--lib/chef/knife/user_create.rb107
-rw-r--r--lib/chef/knife/user_delete.rb44
-rw-r--r--lib/chef/knife/user_dissociate.rb42
-rw-r--r--lib/chef/knife/user_edit.rb52
-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.rb73
-rw-r--r--lib/chef/knife/user_key_delete.rb80
-rw-r--r--lib/chef/knife/user_key_edit.rb83
-rw-r--r--lib/chef/knife/user_key_list.rb73
-rw-r--r--lib/chef/knife/user_key_show.rb80
-rw-r--r--lib/chef/knife/user_list.rb42
-rw-r--r--lib/chef/knife/user_reregister.rb59
-rw-r--r--lib/chef/knife/user_show.rb48
-rw-r--r--lib/chef/knife/xargs.rb282
-rw-r--r--lib/chef/knife/yaml_convert.rb91
-rw-r--r--lib/chef/mixin/get_source_from_package.rb2
-rw-r--r--lib/chef/node.rb41
-rw-r--r--lib/chef/node/attribute.rb10
-rw-r--r--lib/chef/node/immutable_collections.rb13
-rw-r--r--lib/chef/node/mixin/deep_merge_cache.rb18
-rw-r--r--lib/chef/org.rb5
-rw-r--r--lib/chef/policy_builder/policyfile.rb2
-rw-r--r--lib/chef/property.rb18
-rw-r--r--lib/chef/provider.rb2
-rw-r--r--lib/chef/provider/cron.rb2
-rw-r--r--lib/chef/provider/directory.rb12
-rw-r--r--lib/chef/provider/execute.rb3
-rw-r--r--lib/chef/provider/file.rb4
-rw-r--r--lib/chef/provider/git.rb22
-rw-r--r--lib/chef/provider/group/groupadd.rb6
-rw-r--r--lib/chef/provider/group/groupmod.rb6
-rw-r--r--lib/chef/provider/group/pw.rb6
-rw-r--r--lib/chef/provider/ifconfig.rb4
-rw-r--r--lib/chef/provider/link.rb6
-rw-r--r--lib/chef/provider/mount.rb19
-rw-r--r--lib/chef/provider/mount/aix.rb6
-rw-r--r--lib/chef/provider/mount/mount.rb12
-rw-r--r--lib/chef/provider/mount/windows.rb2
-rw-r--r--lib/chef/provider/package.rb110
-rw-r--r--lib/chef/provider/package/apt.rb28
-rw-r--r--lib/chef/provider/package/deb.rb6
-rw-r--r--lib/chef/provider/package/dnf.rb51
-rw-r--r--lib/chef/provider/package/dnf/dnf_helper.py52
-rw-r--r--lib/chef/provider/package/dnf/python_helper.rb29
-rw-r--r--lib/chef/provider/package/freebsd/pkgng.rb4
-rw-r--r--lib/chef/provider/package/portage.rb4
-rw-r--r--lib/chef/provider/package/rubygems.rb28
-rw-r--r--lib/chef/provider/package/windows.rb6
-rw-r--r--lib/chef/provider/package/yum.rb5
-rw-r--r--lib/chef/provider/package/yum/python_helper.rb25
-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__.pycbin12059 -> 0 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/decoder.py354
-rw-r--r--lib/chef/provider/package/yum/simplejson/decoder.pycbin11088 -> 0 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/encoder.py440
-rw-r--r--lib/chef/provider/package/yum/simplejson/encoder.pycbin13588 -> 0 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/scanner.py65
-rw-r--r--lib/chef/provider/package/yum/simplejson/scanner.pycbin2405 -> 0 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/tool.py37
-rw-r--r--lib/chef/provider/package/yum/yum_helper.py114
-rw-r--r--lib/chef/provider/registry_key.rb2
-rw-r--r--lib/chef/provider/route.rb4
-rw-r--r--lib/chef/provider/service.rb12
-rw-r--r--lib/chef/provider/service/aixinit.rb2
-rw-r--r--lib/chef/provider/service/debian.rb2
-rw-r--r--lib/chef/provider/service/freebsd.rb36
-rw-r--r--lib/chef/provider/service/macosx.rb14
-rw-r--r--lib/chef/provider/service/systemd.rb57
-rw-r--r--lib/chef/provider/service/upstart.rb15
-rw-r--r--lib/chef/provider/service/windows.rb24
-rw-r--r--lib/chef/provider/subversion.rb22
-rw-r--r--lib/chef/provider/support/zypper_repo.erb6
-rw-r--r--lib/chef/provider/systemd_unit.rb46
-rw-r--r--lib/chef/provider/template/content.rb10
-rw-r--r--lib/chef/provider/user.rb4
-rw-r--r--lib/chef/provider/user/dscl.rb2
-rw-r--r--lib/chef/provider/user/mac.rb37
-rw-r--r--lib/chef/provider/user/pw.rb2
-rw-r--r--lib/chef/provider/user/windows.rb2
-rw-r--r--lib/chef/provider/windows_script.rb2
-rw-r--r--lib/chef/provider/yum_repository.rb4
-rw-r--r--lib/chef/provider/zypper_repository.rb60
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource.rb43
-rw-r--r--lib/chef/resource/alternatives.rb12
-rw-r--r--lib/chef/resource/apt_package.rb2
-rw-r--r--lib/chef/resource/apt_preference.rb4
-rw-r--r--lib/chef/resource/apt_repository.rb12
-rw-r--r--lib/chef/resource/apt_update.rb9
-rw-r--r--lib/chef/resource/archive_file.rb5
-rw-r--r--lib/chef/resource/breakpoint.rb2
-rw-r--r--lib/chef/resource/build_essential.rb8
-rw-r--r--lib/chef/resource/chef_client_config.rb8
-rw-r--r--lib/chef/resource/chef_client_cron.rb10
-rw-r--r--lib/chef/resource/chef_client_launchd.rb12
-rw-r--r--lib/chef/resource/chef_client_scheduled_task.rb30
-rw-r--r--lib/chef/resource/chef_client_systemd_timer.rb8
-rw-r--r--lib/chef/resource/chef_gem.rb4
-rw-r--r--lib/chef/resource/chef_handler.rb10
-rw-r--r--lib/chef/resource/chef_sleep.rb2
-rw-r--r--lib/chef/resource/chef_vault_secret.rb11
-rw-r--r--lib/chef/resource/chocolatey_config.rb8
-rw-r--r--lib/chef/resource/chocolatey_feature.rb8
-rw-r--r--lib/chef/resource/chocolatey_source.rb14
-rw-r--r--lib/chef/resource/cron/_cron_shared.rb4
-rw-r--r--lib/chef/resource/cron/cron_d.rb11
-rw-r--r--lib/chef/resource/cron_access.rb6
-rw-r--r--lib/chef/resource/dmg_package.rb16
-rw-r--r--lib/chef/resource/dpkg_package.rb2
-rw-r--r--lib/chef/resource/dsc_resource.rb2
-rw-r--r--lib/chef/resource/execute.rb17
-rw-r--r--lib/chef/resource/file.rb4
-rw-r--r--lib/chef/resource/gem_package.rb4
-rw-r--r--lib/chef/resource/group.rb12
-rw-r--r--lib/chef/resource/homebrew_cask.rb33
-rw-r--r--lib/chef/resource/homebrew_package.rb2
-rw-r--r--lib/chef/resource/homebrew_tap.rb11
-rw-r--r--lib/chef/resource/homebrew_update.rb4
-rw-r--r--lib/chef/resource/hostname.rb96
-rw-r--r--lib/chef/resource/http_request.rb4
-rw-r--r--lib/chef/resource/inspec_waiver_file_entry.rb156
-rw-r--r--lib/chef/resource/kernel_module.rb23
-rw-r--r--lib/chef/resource/locale.rb7
-rw-r--r--lib/chef/resource/lwrp_base.rb19
-rw-r--r--lib/chef/resource/macos_userdefaults.rb24
-rw-r--r--lib/chef/resource/mdadm.rb64
-rw-r--r--lib/chef/resource/mount.rb3
-rw-r--r--lib/chef/resource/ohai_hint.rb8
-rw-r--r--lib/chef/resource/openbsd_package.rb17
-rw-r--r--lib/chef/resource/openssl_dhparam.rb3
-rw-r--r--lib/chef/resource/openssl_ec_private_key.rb8
-rw-r--r--lib/chef/resource/openssl_ec_public_key.rb4
-rw-r--r--lib/chef/resource/openssl_rsa_private_key.rb6
-rw-r--r--lib/chef/resource/openssl_rsa_public_key.rb4
-rw-r--r--lib/chef/resource/openssl_x509_certificate.rb9
-rw-r--r--lib/chef/resource/openssl_x509_crl.rb4
-rw-r--r--lib/chef/resource/openssl_x509_request.rb4
-rw-r--r--lib/chef/resource/osx_profile.rb20
-rw-r--r--lib/chef/resource/plist.rb16
-rw-r--r--lib/chef/resource/powershell_package_source.rb6
-rw-r--r--lib/chef/resource/powershell_script.rb2
-rw-r--r--lib/chef/resource/reboot.rb47
-rw-r--r--lib/chef/resource/remote_directory.rb6
-rw-r--r--lib/chef/resource/remote_file.rb6
-rw-r--r--lib/chef/resource/rhsm_errata.rb19
-rw-r--r--lib/chef/resource/rhsm_errata_level.rb17
-rw-r--r--lib/chef/resource/rhsm_register.rb18
-rw-r--r--lib/chef/resource/rhsm_repo.rb23
-rw-r--r--lib/chef/resource/rhsm_subscription.rb8
-rw-r--r--lib/chef/resource/ruby.rb6
-rw-r--r--lib/chef/resource/ruby_block.rb2
-rw-r--r--lib/chef/resource/scm/git.rb2
-rw-r--r--lib/chef/resource/ssh_known_hosts_entry.rb11
-rw-r--r--lib/chef/resource/sudo.rb20
-rw-r--r--lib/chef/resource/swap_file.rb8
-rw-r--r--lib/chef/resource/sysctl.rb8
-rw-r--r--lib/chef/resource/systemd_unit.rb4
-rw-r--r--lib/chef/resource/template.rb6
-rw-r--r--lib/chef/resource/timezone.rb4
-rw-r--r--lib/chef/resource/user/dscl_user.rb35
-rw-r--r--lib/chef/resource/user/mac_user.rb2
-rw-r--r--lib/chef/resource/user/windows_user.rb5
-rw-r--r--lib/chef/resource/user_ulimit.rb4
-rw-r--r--lib/chef/resource/windows_ad_join.rb8
-rw-r--r--lib/chef/resource/windows_audit_policy.rb4
-rw-r--r--lib/chef/resource/windows_auto_run.rb7
-rw-r--r--lib/chef/resource/windows_certificate.rb346
-rw-r--r--lib/chef/resource/windows_defender.rb163
-rw-r--r--lib/chef/resource/windows_defender_exclusion.rb125
-rw-r--r--lib/chef/resource/windows_dfs_folder.rb8
-rw-r--r--lib/chef/resource/windows_dfs_namespace.rb8
-rw-r--r--lib/chef/resource/windows_dfs_server.rb4
-rw-r--r--lib/chef/resource/windows_dns_record.rb8
-rw-r--r--lib/chef/resource/windows_dns_zone.rb8
-rw-r--r--lib/chef/resource/windows_env.rb11
-rw-r--r--lib/chef/resource/windows_feature.rb12
-rw-r--r--lib/chef/resource/windows_feature_dism.rb12
-rw-r--r--lib/chef/resource/windows_feature_powershell.rb6
-rw-r--r--lib/chef/resource/windows_firewall_profile.rb8
-rw-r--r--lib/chef/resource/windows_firewall_rule.rb29
-rw-r--r--lib/chef/resource/windows_font.rb8
-rw-r--r--lib/chef/resource/windows_pagefile.rb173
-rw-r--r--lib/chef/resource/windows_path.rb8
-rw-r--r--lib/chef/resource/windows_printer.rb141
-rw-r--r--lib/chef/resource/windows_printer_port.rb115
-rw-r--r--lib/chef/resource/windows_security_policy.rb96
-rw-r--r--lib/chef/resource/windows_share.rb35
-rw-r--r--lib/chef/resource/windows_shortcut.rb10
-rw-r--r--lib/chef/resource/windows_task.rb26
-rw-r--r--lib/chef/resource/windows_uac.rb8
-rw-r--r--lib/chef/resource/windows_user_privilege.rb8
-rw-r--r--lib/chef/resource/windows_workgroup.rb7
-rw-r--r--lib/chef/resource/yum_package.rb20
-rw-r--r--lib/chef/resource/yum_repository.rb5
-rw-r--r--lib/chef/resource/zypper_package.rb8
-rw-r--r--lib/chef/resource/zypper_repository.rb36
-rw-r--r--lib/chef/resource_builder.rb10
-rw-r--r--lib/chef/resource_collection/resource_set.rb2
-rw-r--r--lib/chef/resource_inspector.rb6
-rw-r--r--lib/chef/resource_reporter.rb1
-rw-r--r--lib/chef/resources.rb6
-rw-r--r--lib/chef/run_lock.rb2
-rw-r--r--lib/chef/runner.rb2
-rw-r--r--lib/chef/shell.rb33
-rw-r--r--lib/chef/shell/ext.rb6
-rw-r--r--lib/chef/user.rb1
-rw-r--r--lib/chef/user_v1.rb7
-rw-r--r--lib/chef/util/dsc/configuration_generator.rb3
-rw-r--r--lib/chef/util/dsc/local_configuration_manager.rb2
-rw-r--r--lib/chef/version.rb2
-rw-r--r--lib/chef/version_string.rb2
-rw-r--r--lib/chef/win32/api.rb11
-rw-r--r--lib/chef/win32/registry.rb6
410 files changed, 2998 insertions, 21549 deletions
diff --git a/lib/chef/action_collection.rb b/lib/chef/action_collection.rb
index 1ac47630a9..82a4ebb037 100644
--- a/lib/chef/action_collection.rb
+++ b/lib/chef/action_collection.rb
@@ -87,13 +87,11 @@ class Chef
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
@@ -118,17 +116,17 @@ class Chef
self.class.new(events, run_context, subrecords)
end
+ def resources
+ action_records.map(&:new_resource)
+ 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
+ # this hook is now poorly named since it is just a callback that lets other consumers snag a reference to the action_collection
run_context.events.enqueue(:action_collection_registration, self)
@run_context = run_context
end
@@ -139,7 +137,7 @@ class Chef
# @params object [Object] callers should call with `self`
#
def register(object)
- consumers << object
+ Chef::Log.warn "the action collection no longer requires registration at #{caller[0]}"
end
# End of an unsuccessful converge used to fire off detect_unprocessed_resources.
@@ -147,8 +145,6 @@ class Chef
# (see EventDispatch::Base#)
#
def converge_failed(exception)
- return if consumers.empty?
-
detect_unprocessed_resources
end
@@ -159,8 +155,6 @@ class Chef
# (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
@@ -170,8 +164,6 @@ class Chef
# (see EventDispatch::Base#)
#
def resource_current_state_loaded(new_resource, action, current_resource)
- return if consumers.empty?
-
current_record.current_resource = current_resource
end
@@ -181,8 +173,6 @@ class Chef
# (see EventDispatch::Base#)
#
def resource_after_state_loaded(new_resource, action, after_resource)
- return if consumers.empty?
-
current_record.after_resource = after_resource
end
@@ -191,8 +181,6 @@ class Chef
# (see EventDispatch::Base#)
#
def resource_up_to_date(new_resource, action)
- return if consumers.empty?
-
current_record.status = :up_to_date
end
@@ -201,8 +189,6 @@ class Chef
# (see EventDispatch::Base#)
#
def resource_skipped(resource, action, conditional)
- return if consumers.empty?
-
current_record.status = :skipped
current_record.conditional = conditional
end
@@ -212,8 +198,6 @@ class Chef
# (see EventDispatch::Base#)
#
def resource_updated(new_resource, action)
- return if consumers.empty?
-
current_record.status = :updated
end
@@ -222,8 +206,6 @@ class Chef
# (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
@@ -234,8 +216,6 @@ class Chef
# (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
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 117f498831..356c4a4a30 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -310,7 +310,7 @@ class Chef
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)
+ # TERM signal is received (exit gracefully)
trap("TERM") do
logger.debug("SIGTERM received during converge," +
" finishing converge to exit normally (send SIGINT to terminate immediately)")
diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb
index ad8e8b69c2..b2f98d5f2f 100644
--- a/lib/chef/application/base.rb
+++ b/lib/chef/application/base.rb
@@ -297,6 +297,21 @@ class Chef::Application::Base < Chef::Application
long: "--named-run-list NAMED_RUN_LIST",
description: "Use a policyfile's named run list instead of the default run list."
+ option :slow_report,
+ long: "--[no-]slow-report [COUNT]",
+ description: "List the slowest resources at the end of the run (default: 10).",
+ boolean: true,
+ default: false,
+ proc: lambda { |argument|
+ if argument.nil?
+ true
+ elsif argument == false
+ false
+ else
+ Integer(argument)
+ end
+ }
+
IMMEDIATE_RUN_SIGNAL = "1".freeze
RECONFIGURE_SIGNAL = "H".freeze
@@ -368,7 +383,7 @@ class Chef::Application::Base < Chef::Application
FileUtils.cp(url, path)
elsif URI::DEFAULT_PARSER.make_regexp.match?(url)
File.open(path, "wb") do |f|
- open(url) do |r|
+ URI.open(url) do |r|
f.write(r.read)
end
end
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
deleted file mode 100644
index 7906ce6eaa..0000000000
--- a/lib/chef/application/knife.rb
+++ /dev/null
@@ -1,234 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-require_relative "../application"
-require "mixlib/log"
-require "ohai/config"
-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".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) }
-
- option :config_option,
- long: "--config-option OPTION=VALUE",
- description: "Override a single configuration option.",
- proc: lambda { |option, existing|
- (existing ||= []) << option
- existing
- }
-
- verbosity_level = 0
- option :verbosity,
- short: "-V",
- 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."
-
- option :environment,
- 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"]
-
- option :disable_editing,
- 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 help message.",
- on: :tail,
- boolean: true
-
- option :node_name,
- short: "-u USER",
- long: "--user USER",
- description: "#{ChefUtils::Dist::Server::PRODUCT} API client username."
-
- option :client_key,
- 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: "#{ChefUtils::Dist::Server::PRODUCT} URL."
-
- option :yes,
- short: "-y",
- long: "--yes",
- description: "Say yes to all prompts for confirmation."
-
- option :defaults,
- long: "--defaults",
- description: "Accept default values for all questions."
-
- option :print_after,
- 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.",
- 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 #{ChefUtils::Dist::Server::PRODUCT}.",
- 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 :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 :fips,
- 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
- Chef::Knife.run(ARGV, options)
- exit 0
- end
-
- private
-
- def quiet_traps
- trap("TERM") do
- exit 1
- end
-
- trap("INT") do
- exit 2
- end
- end
-
- def validate_and_parse_options
- # Checking ARGV validity *before* parse_options because parse_options
- # mangles ARGV in some situations
- if no_command_given?
- print_help_and_exit(1, NO_COMMAND_GIVEN)
- elsif no_subcommand_given?
- if want_help? || want_version?
- print_help_and_exit(0)
- else
- print_help_and_exit(2, NO_COMMAND_GIVEN)
- end
- end
- end
-
- def no_subcommand_given?
- ARGV[0] =~ /^-/
- end
-
- def no_command_given?
- ARGV.empty?
- end
-
- def want_help?
- ARGV[0] =~ /^(--help|-h)$/
- end
-
- def want_version?
- ARGV[0] =~ /^(--version|-v)$/
- end
-
- def print_help_and_exit(exitcode = 1, fatal_message = nil)
- Chef::Log.error(fatal_message) if fatal_message
-
- begin
- parse_options
- rescue OptionParser::InvalidOption => e
- puts "#{e}\n"
- end
-
- 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
- end
-
-end
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
deleted file mode 100644
index 8975556f75..0000000000
--- a/lib/chef/application/windows_service.rb
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# Author:: Christopher Maier (<maier@lambda.local>)
-# Copyright:: Copyright (c) Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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"
-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_relative "../mixin/shell_out"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Application
- class WindowsService < ::Win32::Daemon
- include Mixlib::CLI
- include Chef::Mixin::ShellOut
-
- option :config_file,
- 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."
-
- 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 :interval,
- 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 ||= "#{Chef::Config.c_chef_dir}/client.log".freeze
-
- def service_init
- @service_action_mutex = Mutex.new
- @service_signal = ConditionVariable.new
-
- reconfigure
- Chef::Log.info("#{ChefUtils::Dist::Infra::CLIENT} Service initialized")
- end
-
- def service_main(*startup_parameters)
- # Chef::Config is initialized during service_init
- # Set the initial timeout to splay sleep time
- timeout = rand Chef::Config[:splay]
-
- while running?
- # Grab the service_action_mutex to make a chef-client run
- @service_action_mutex.synchronize do
-
- 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.trace("Giving signal callbacks some time to exit...")
- sleep 1
- Chef::Log.trace("Exiting service...")
- end
-
- ################################################################################
- # Control Signal Callback Methods
- ################################################################################
-
- def service_stop
- run_warning_displayed = false
- Chef::Log.info("STOP request from operating system.")
- loop do
- # See if a run is in flight
- if @service_action_mutex.try_lock
- # Run is not in flight. Wake up service_main to exit.
- @service_signal.signal
- @service_action_mutex.unlock
- break
- else
- unless run_warning_displayed
- 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.trace("Waiting for #{ChefUtils::Dist::Infra::PRODUCT} run...")
- sleep 1
- end
- end
- Chef::Log.info("Service is stopping....")
- end
-
- def service_pause
- Chef::Log.info("PAUSE request from operating system.")
-
- # We don't need to wake up the service_main if it's waiting
- # since this is a PAUSE signal.
-
- if @service_action_mutex.locked?
- 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....")
- end
- end
-
- def service_resume
- # We don't need to wake up the service_main if it's waiting
- # since this is a RESUME signal.
-
- Chef::Log.info("RESUME signal received from the OS.")
- Chef::Log.info("Service is resuming....")
- end
-
- def service_shutdown
- Chef::Log.info("SHUTDOWN signal received from the OS.")
-
- # Treat shutdown similar to stop.
-
- service_stop
- end
-
- ################################################################################
- # Internal Methods
- ################################################################################
-
- private
-
- # Initializes Chef::Client instance and runs it
- def run_chef_client
- # 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.
-
- 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 #{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)
- Chef::Config.from_file(config_file_path)
- Chef::Config.merge!(config)
- end
-
- # Lifted from Chef::Application, with addition of optional startup parameters
- # for playing nicely with Windows Services
- def reconfigure(startup_parameters = [])
- configure_chef startup_parameters
- configure_logging
-
- 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
-
- Chef::Config[:interval] ||= 1800
- end
-
- # Lifted from application.rb
- # See application.rb for related comments.
-
- def configure_logging
- Chef::Log.init(MonoLogger.new(resolve_log_location))
- if want_additional_logger?
- configure_stdout_logger
- end
- Chef::Log.level = resolve_log_level
- 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]
- end
-
- # Use of output formatters is assumed if `force_formatter` is set or if
- # `force_logger` is not set
- def using_output_formatter?
- Chef::Config[:force_formatter] || !Chef::Config[:force_logger]
- end
-
- def auto_log_level?
- Chef::Config[:log_level] == :auto
- end
-
- def resolve_log_location
- # STDOUT is the default log location, but makes no sense for a windows service
- Chef::Config[:log_location] == STDOUT ? DEFAULT_LOG_LOCATION : Chef::Config[:log_location]
- end
-
- # if log_level is `:auto`, convert it to :warn (when using output formatter)
- # or :info (no output formatter). See also +using_output_formatter?+
- def resolve_log_level
- if auto_log_level?
- if using_output_formatter?
- :warn
- else
- :info
- end
- else
- Chef::Config[:log_level]
- end
- end
-
- def configure_chef(startup_parameters)
- # Bit of a hack ahead:
- # It is possible to specify a service's binary_path_name with arguments, like "foo.exe -x argX".
- # It is also possible to specify startup parameters separately, either via the Services manager
- # or by using the registry (I think).
-
- # In order to accommodate all possible sources of parameterization, we first parse any command line
- # arguments. We then parse any startup parameters. This works, because Mixlib::CLI reuses its internal
- # 'config' hash; thus, anything in startup parameters will override any command line parameters that
- # might be set via the service's binary_path_name
- #
- # All these parameters then get layered on top of those from Chef::Config
-
- parse_options # Operates on ARGV by default
- parse_options startup_parameters
-
- begin
- case config[:config_file]
- 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 instead.")
- Chef::Log.warn("*****************************************")
-
- Chef::Config.merge!(config)
- rescue SocketError
- 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}")
- rescue Exception => error
- Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}")
- end
- end
-
- 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
- Chef::Application::WindowsService.mainloop
-end
diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb
deleted file mode 100644
index 4f0de26411..0000000000
--- a/lib/chef/application/windows_service_manager.rb
+++ /dev/null
@@ -1,205 +0,0 @@
-#
-# 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.
-#
-
-if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
- require "win32/service"
-end
-require_relative "../config"
-require "mixlib/cli" unless defined?(Mixlib::CLI)
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Application
- #
- # This class is used to create and manage a windows service.
- # Service should be created using Daemon class from
- # win32/service gem.
- # For an example see: Chef::Application::WindowsService
- #
- # Outside programs are expected to use this class to manage
- # windows services.
- #
- class WindowsServiceManager
- include Mixlib::CLI
-
- option :action,
- 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: "#{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 #{ChefUtils::Dist::Infra::SHORT}-service."
-
- option :help,
- 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 #{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
- # anti-pattern :(
- super()
-
- raise ArgumentError, "Service definition is not provided" if service_options.nil?
-
- required_options = %i{service_name service_display_name service_description service_file_path}
-
- required_options.each do |req_option|
- unless service_options.key?(req_option)
- raise ArgumentError, "Service definition doesn't contain required option #{req_option}"
- end
- end
-
- @service_name = service_options[:service_name]
- @service_display_name = service_options[:service_display_name]
- @service_description = service_options[:service_description]
- @service_file_path = service_options[:service_file_path]
- @service_start_name = service_options[:run_as_user]
- @password = service_options[:run_as_password]
- @delayed_start = service_options[:delayed_start]
- @dependencies = service_options[:dependencies]
- end
-
- def run(params = ARGV)
- parse_options(params)
-
- case config[:action]
- when "install"
- if service_exists?
- puts "Service #{@service_name} already exists on the system."
- else
- ruby = File.join(RbConfig::CONFIG["bindir"], "ruby")
-
- opts = ""
- opts << " -c #{config[:config_file]}" if config[:config_file]
- opts << " -L #{config[:log_location]}" if config[:log_location]
-
- # Quote the full paths to deal with possible spaces in the path name.
- # Also ensure all forward slashes are backslashes
- 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,
- # 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
- )
- 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"
- if !service_exists?
- puts "Service #{@service_name} doesn't exist on the system."
- else
- puts "State of #{@service_name} service is: #{current_state}"
- end
- when "start"
- # TODO: allow override of startup parameters here?
- take_action("start", RUNNING)
- when "stop"
- take_action("stop", STOPPED)
- when "uninstall", "delete"
- take_action("stop", STOPPED)
- unless service_exists?
- puts "Service #{@service_name} doesn't exist on the system."
- else
- ::Win32::Service.delete(@service_name)
- puts "Service #{@service_name} deleted"
- end
- when "pause"
- take_action("pause", PAUSED)
- when "resume"
- take_action("resume", RUNNING)
- end
- end
-
- private
-
- # Just some state constants
- STOPPED = "stopped".freeze
- RUNNING = "running".freeze
- PAUSED = "paused".freeze
-
- def service_exists?
- ::Win32::Service.exists?(@service_name)
- end
-
- def take_action(action = nil, desired_state = nil)
- if service_exists?
- if current_state != desired_state
- ::Win32::Service.send(action, @service_name)
- wait_for_state(desired_state)
- puts "Service '#{@service_name}' is now '#{current_state}'."
- else
- puts "Service '#{@service_name}' is already '#{desired_state}'."
- end
- else
- puts "Cannot '#{action}' service '#{@service_name}'"
- puts "Service #{@service_name} doesn't exist on the system."
- end
- end
-
- def current_state
- ::Win32::Service.status(@service_name).current_state
- end
-
- # Helper method that waits for a status to change its state since state
- # changes aren't usually instantaneous.
- def wait_for_state(desired_state)
- while current_state != desired_state
- puts "One moment... #{current_state}"
- sleep 1
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/applications.rb b/lib/chef/applications.rb
index a30b765c77..8f7f418d3f 100644
--- a/lib/chef/applications.rb
+++ b/lib/chef/applications.rb
@@ -1,4 +1,3 @@
require_relative "application/client"
-require_relative "application/knife"
require_relative "application/solo"
require_relative "application/apply"
diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb
index 1e3aa137cd..16d9bd93f4 100644
--- a/lib/chef/chef_fs/command_line.rb
+++ b/lib/chef/chef_fs/command_line.rb
@@ -19,6 +19,9 @@
require_relative "file_system"
require_relative "file_system/exceptions"
require_relative "../util/diff"
+require "chef-utils/parallel_map" unless defined?(ChefUtils::ParallelMap)
+
+using ChefUtils::ParallelMap
class Chef
module ChefFS
@@ -140,7 +143,7 @@ class Chef
end
def self.diff(pattern, old_root, new_root, recurse_depth, get_content)
- Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.list_pairs(pattern, old_root, new_root)) do |old_entry, new_entry|
+ Chef::ChefFS::FileSystem.list_pairs(pattern, old_root, new_root).parallel_map do |old_entry, new_entry|
diff_entries(old_entry, new_entry, recurse_depth, get_content)
end.flatten(1)
end
@@ -153,7 +156,7 @@ class Chef
if recurse_depth == 0
[ [ :common_subdirectories, old_entry, new_entry ] ]
else
- Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry)) do |old_child, new_child|
+ Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry).parallel_map 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
diff --git a/lib/chef/chef_fs/file_pattern.rb b/lib/chef/chef_fs/file_pattern.rb
index 37e72f379b..7e7a58e312 100644
--- a/lib/chef/chef_fs/file_pattern.rb
+++ b/lib/chef/chef_fs/file_pattern.rb
@@ -255,7 +255,7 @@ class Chef
end
def self.regexp_escape_characters
- [ "[", '\\', "^", "$", ".", "|", "?", "*", "+", "(", ")", "{", "}" ]
+ [ "[", "\\", "^", "$", ".", "|", "?", "*", "+", "(", ")", "{", "}" ]
end
def self.pattern_to_regexp(pattern)
@@ -281,7 +281,7 @@ class Chef
exact = nil
regexp << "."
else
- if part[0, 1] == '\\' && part.length == 2
+ 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] unless exact.nil?
if regexp_escape_characters.include?(part[1, 1])
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index aadfc9807c..5c4278c100 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -18,7 +18,9 @@
require_relative "path_utils"
require_relative "file_system/exceptions"
-require_relative "parallelizer"
+require "chef-utils/parallel_map" unless defined?(ChefUtils::ParallelMap)
+
+using ChefUtils::ParallelMap
class Chef
module ChefFS
@@ -70,8 +72,8 @@ class Chef
# Otherwise, go through all children and find any matches
elsif entry.dir?
- results = Parallelizer.parallelize(entry.children) { |child| Chef::ChefFS::FileSystem.list(child, pattern) }
- results.flatten(1).each(&block)
+ results = entry.children.parallel_map { |child| Chef::ChefFS::FileSystem.list(child, pattern) }
+ results.flat_each(&block)
end
end
end
@@ -138,7 +140,7 @@ class Chef
def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui = nil, format_path = nil)
found_result = false
error = false
- parallel_do(list_pairs(pattern, src_root, dest_root)) do |src, dest|
+ list_pairs(pattern, src_root, dest_root).parallel_each do |src, dest|
found_result = true
new_dest_parent = get_or_create_parent(dest, options, ui, format_path)
child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
@@ -292,7 +294,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
@@ -319,7 +321,7 @@ class Chef
end
# Directory creation is recursive.
if recurse_depth != 0
- parallel_do(src_entry.children) do |src_child|
+ src_entry.children.parallel_each do |src_child|
new_dest_child = new_dest_dir.child(src_child.name)
child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
error ||= child_error
@@ -356,7 +358,7 @@ class Chef
if dest_entry.dir?
# If both are directories, recurse into their children
if recurse_depth != 0
- parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
+ child_pairs(src_entry, dest_entry).parallel_each do |src_child, dest_child|
child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
error ||= child_error
end
@@ -423,9 +425,6 @@ class Chef
parent
end
- def parallel_do(enum, options = {}, &block)
- Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block)
- end
end
end
end
diff --git a/lib/chef/chef_fs/knife.rb b/lib/chef/chef_fs/knife.rb
deleted file mode 100644
index ba993beee4..0000000000
--- a/lib/chef/chef_fs/knife.rb
+++ /dev/null
@@ -1,160 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@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"
-require "pathname" unless defined?(Pathname)
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- module ChefFS
- class Knife < Chef::Knife
- # Workaround for CHEF-3932
- def self.deps
- super do
- require_relative "../config"
- require_relative "parallelizer"
- require_relative "config"
- require_relative "file_pattern"
- require_relative "path_utils"
- yield
- end
- end
-
- def self.inherited(c)
- super
-
- # Ensure we always get to do our includes, whether subclass calls deps or not
- c.deps do
- end
- end
-
- option :repo_mode,
- 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 #{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)"
-
- def configure_chef
- super
- Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode]
- Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
-
- # --chef-repo-path forcibly overrides all other paths
- if config[:chef_repo_path]
- Chef::Config[:chef_repo_path] = config[:chef_repo_path]
- Chef::ChefFS::Config::INFLECTIONS.each_value do |variable_name|
- Chef::Config.delete("#{variable_name}_path".to_sym)
- end
- end
-
- @chef_fs_config = Chef::ChefFS::Config.new(Chef::Config, Dir.pwd, config, ui)
-
- Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
- end
-
- def chef_fs
- @chef_fs_config.chef_fs
- end
-
- def create_chef_fs
- @chef_fs_config.create_chef_fs
- end
-
- def local_fs
- @chef_fs_config.local_fs
- end
-
- def create_local_fs
- @chef_fs_config.create_local_fs
- end
-
- def pattern_args
- @pattern_args ||= pattern_args_from(name_args)
- end
-
- def pattern_args_from(args)
- args.map { |arg| pattern_arg_from(arg) }
- end
-
- def pattern_arg_from(arg)
- inferred_path = nil
- if Chef::ChefFS::PathUtils.is_absolute?(arg)
- # We should be able to use this as-is - but the user might have incorrectly provided
- # us with a path that is based off of the OS root path instead of the Chef-FS root.
- # Do a quick and dirty sanity check.
- if possible_server_path = @chef_fs_config.server_path(arg)
- ui.warn("The absolute path provided is suspicious: #{arg}")
- ui.warn("If you wish to refer to a file location, please provide a path that is rooted at the chef-repo.")
- ui.warn("Consider writing '#{possible_server_path}' instead of '#{arg}'")
- end
- # Use the original path because we can't be sure.
- inferred_path = arg
- elsif arg[0, 1] == "~"
- # Let's be nice and fix it if possible - but warn the user.
- ui.warn("A path relative to a user home directory has been provided: #{arg}")
- ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
- inferred_path = @chef_fs_config.server_path(arg)
- ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
- elsif Pathname.new(arg).absolute?
- # It is definitely a system absolute path (such as C:\ or \\foo\bar) but it cannot be
- # interpreted as a Chef-FS absolute path. Again attempt to be nice but warn the user.
- ui.warn("An absolute file system path that isn't a server path was provided: #{arg}")
- ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
- inferred_path = @chef_fs_config.server_path(arg)
- ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
- elsif @chef_fs_config.base_path.nil?
- # These are all relative paths. We can't resolve and root paths unless we are in the
- # chef repo.
- ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path.")
- ui.error("Current working directory is '#{@chef_fs_config.cwd}'.")
- exit(1)
- else
- inferred_path = Chef::ChefFS::PathUtils.join(@chef_fs_config.base_path, arg)
- end
- Chef::ChefFS::FilePattern.new(inferred_path)
- end
-
- def format_path(entry)
- @chef_fs_config.format_path(entry)
- end
-
- def parallelize(inputs, options = {}, &block)
- Chef::ChefFS::Parallelizer.parallelize(inputs, options, &block)
- end
-
- def discover_repo_dir(dir)
- %w{.chef cookbooks data_bags environments roles}.each do |subdir|
- return dir if File.directory?(File.join(dir, subdir))
- end
- # If this isn't it, check the parent
- parent = File.dirname(dir)
- if parent && parent != dir
- discover_repo_dir(parent)
- else
- nil
- end
- end
- end
- end
-end
diff --git a/lib/chef/chef_fs/parallelizer.rb b/lib/chef/chef_fs/parallelizer.rb
deleted file mode 100644
index c4d17a842d..0000000000
--- a/lib/chef/chef_fs/parallelizer.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-require_relative "parallelizer/parallel_enumerable"
-
-class Chef
- module ChefFS
- # Tries to balance several guarantees, in order of priority:
- # - don't get deadlocked
- # - provide results in desired order
- # - provide results as soon as they are available
- # - process input as soon as possible
- class Parallelizer
- @@parallelizer = nil
- @@threads = 0
-
- def self.threads=(value)
- @@threads = value
- @@parallelizer.resize(value) if @@parallelizer
- end
-
- def self.parallelizer
- @@parallelizer ||= Parallelizer.new(@@threads)
- end
-
- def self.parallelize(enumerable, options = {}, &block)
- parallelizer.parallelize(enumerable, options, &block)
- end
-
- def self.parallel_do(enumerable, options = {}, &block)
- parallelizer.parallel_do(enumerable, options, &block)
- end
-
- def initialize(num_threads)
- @tasks = Queue.new
- @threads = []
- @stop_thread = {}
- resize(num_threads)
- end
-
- def num_threads
- @threads.size
- end
-
- def parallelize(enumerable, options = {}, &block)
- ParallelEnumerable.new(@tasks, enumerable, options, &block)
- end
-
- def parallel_do(enumerable, options = {}, &block)
- ParallelEnumerable.new(@tasks, enumerable, options.merge(ordered: false), &block).wait
- end
-
- def stop(wait = true, timeout = nil)
- resize(0, wait, timeout)
- end
-
- def resize(to_threads, wait = true, timeout = nil)
- if to_threads < num_threads
- threads_to_stop = @threads[to_threads..num_threads - 1]
- @threads = @threads.slice(0, to_threads)
- threads_to_stop.each do |thread|
- @stop_thread[thread] = true
- end
-
- if wait
- start_time = Time.now
- threads_to_stop.each do |thread|
- thread_timeout = timeout ? timeout - (Time.now - start_time) : nil
- thread.join(thread_timeout)
- end
- end
-
- else
- num_threads.upto(to_threads - 1) do |i|
- @threads[i] = Thread.new(&method(:worker_loop))
- end
- end
- end
-
- def kill
- @threads.each do |thread|
- Thread.kill(thread)
- @stop_thread.delete(thread)
- end
- @threads = []
- end
-
- private
-
- def worker_loop
- until @stop_thread[Thread.current]
- begin
- task = @tasks.pop
- task.call
- rescue
- puts "ERROR #{$!}"
- puts $!.backtrace
- end
- end
- ensure
- @stop_thread.delete(Thread.current)
- end
- end
- end
-end
diff --git a/lib/chef/chef_fs/parallelizer/flatten_enumerable.rb b/lib/chef/chef_fs/parallelizer/flatten_enumerable.rb
deleted file mode 100644
index 84db2c2053..0000000000
--- a/lib/chef/chef_fs/parallelizer/flatten_enumerable.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class Chef
- module ChefFS
- class Parallelizer
- class FlattenEnumerable
- include Enumerable
-
- def initialize(enum, levels = nil)
- @enum = enum
- @levels = levels
- end
-
- attr_reader :enum
- attr_reader :levels
-
- def each(&block)
- enum.each do |value|
- flatten(value, levels, &block)
- end
- end
-
- private
-
- def flatten(value, levels, &block)
- if levels != 0 && value.respond_to?(:each) && !value.is_a?(String)
- value.each do |child|
- flatten(child, levels.nil? ? levels : levels - 1, &block)
- end
- else
- yield(value)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
deleted file mode 100644
index 5fafc5c88f..0000000000
--- a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
+++ /dev/null
@@ -1,278 +0,0 @@
-require_relative "flatten_enumerable"
-
-class Chef
- module ChefFS
- class Parallelizer
- class ParallelEnumerable
- include Enumerable
-
- # options:
- # :ordered [true|false] - whether the output should stay in the same order
- # as the input (even though it may not actually be processed in that
- # order). Default: true
- # :stop_on_exception [true|false] - if true, when an exception occurs in either
- # input or output, we wait for any outstanding processing to complete,
- # but will not process any new inputs. Default: false
- # :main_thread_processing [true|false] - whether the main thread pulling
- # on each() is allowed to process inputs. Default: true
- # NOTE: If you set this to false, parallelizer.kill will stop each()
- # in its tracks, so you need to know for sure that won't happen.
- def initialize(parent_task_queue, input_enumerable, options = {}, &block)
- @parent_task_queue = parent_task_queue
- @input_enumerable = input_enumerable
- @options = options
- @block = block
-
- @unconsumed_input = Queue.new
- @in_process = {}
- @unconsumed_output = Queue.new
- end
-
- attr_reader :parent_task_queue
- attr_reader :input_enumerable
- attr_reader :options
- attr_reader :block
-
- def each
- each_with_input do |output, index, input, type|
- yield output
- end
- end
-
- def each_with_index
- each_with_input do |output, index, input|
- yield output, index
- end
- end
-
- def each_with_input
- exception = nil
- each_with_exceptions do |output, index, input, type|
- if type == :exception
- if @options[:ordered] == false
- exception ||= output
- else
- raise output
- end
- else
- yield output, index, input
- end
- end
- raise exception if exception
- end
-
- def each_with_exceptions(&block)
- if @options[:ordered] == false
- each_with_exceptions_unordered(&block)
- else
- each_with_exceptions_ordered(&block)
- end
- end
-
- def wait
- exception = nil
- each_with_exceptions_unordered do |output, index, input, type|
- exception ||= output if type == :exception
- end
- raise exception if exception
- end
-
- # Enumerable methods
- def restricted_copy(enumerable)
- ParallelEnumerable.new(@parent_task_queue, enumerable, @options, &@block)
- end
-
- alias :original_count :count
-
- def count(*args, &block)
- if args.size == 0 && block.nil?
- @input_enumerable.count
- else
- original_count(*args, &block)
- end
- end
-
- def first(n = nil)
- if n
- restricted_copy(@input_enumerable.first(n)).to_a
- else
- first(1)[0]
- end
- end
-
- def drop(n)
- restricted_copy(@input_enumerable.drop(n)).to_a
- end
-
- def flatten(levels = nil)
- FlattenEnumerable.new(self, levels)
- end
-
- def take(n)
- restricted_copy(@input_enumerable.take(n)).to_a
- end
-
- if Enumerable.method_defined?(:lazy)
- class RestrictedLazy
- def initialize(parallel_enumerable, actual_lazy)
- @parallel_enumerable = parallel_enumerable
- @actual_lazy = actual_lazy
- end
-
- def drop(*args, &block)
- input = @parallel_enumerable.input_enumerable.lazy.drop(*args, &block)
- @parallel_enumerable.restricted_copy(input)
- end
-
- def take(*args, &block)
- input = @parallel_enumerable.input_enumerable.lazy.take(*args, &block)
- @parallel_enumerable.restricted_copy(input)
- end
-
- def method_missing(method, *args, &block)
- @actual_lazy.send(:method, *args, &block)
- end
- end
-
- alias :original_lazy :lazy
-
- def lazy
- RestrictedLazy.new(self, original_lazy)
- end
- end
-
- private
-
- def each_with_exceptions_unordered
- if @each_running
- raise "each() called on parallel enumerable twice simultaneously! Bad mojo"
- end
-
- @each_running = true
- begin
- # Grab all the inputs, yielding any responses during enumeration
- # in case the enumeration itself takes time
- begin
- @input_enumerable.each_with_index do |input, index|
- @unconsumed_input.push([ input, index ])
- @parent_task_queue.push(method(:process_one))
-
- stop_processing_input = false
- until @unconsumed_output.empty?
- output, index, input, type = @unconsumed_output.pop
- yield output, index, input, type
- if type == :exception && @options[:stop_on_exception]
- stop_processing_input = true
- break
- end
- end
-
- if stop_processing_input
- break
- end
- end
- rescue
- # We still want to wait for the rest of the outputs to process
- @unconsumed_output.push([$!, nil, nil, :exception])
- if @options[:stop_on_exception]
- @unconsumed_input.clear
- end
- end
-
- until finished?
- # yield thread to others (for 1.8.7)
- if @unconsumed_output.empty?
- sleep(0.01)
- end
-
- yield @unconsumed_output.pop until @unconsumed_output.empty?
-
- # If no one is working on our tasks and we're allowed to
- # work on them in the main thread, process an input to
- # move things forward.
- if @in_process.size == 0 && !(@options[:main_thread_processing] == false)
- process_one
- end
- end
- rescue
- # 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.
- unless finished?
- stop
- end
- raise
- ensure
- @each_running = false
- end
- end
-
- def each_with_exceptions_ordered
- next_to_yield = 0
- unconsumed = {}
- each_with_exceptions_unordered do |output, index, input, type|
- unconsumed[index] = [ output, input, type ]
- while unconsumed[next_to_yield]
- input_output = unconsumed.delete(next_to_yield)
- yield input_output[0], next_to_yield, input_output[1], input_output[2]
- next_to_yield += 1
- end
- end
- input_exception = unconsumed.delete(nil)
- if input_exception
- yield input_exception[0], next_to_yield, input_exception[1], input_exception[2]
- end
- end
-
- def stop
- @unconsumed_input.clear
- sleep(0.05) while @in_process.size > 0
- @unconsumed_output.clear
- end
-
- #
- # This is thread safe only if called from the main thread pulling on each().
- # 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 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.
- # Thus, 1+2 means no more output will ever show up, and 3 means we've passed all
- # existing outputs to the user.
- #
- def finished?
- @unconsumed_input.empty? && @in_process.size == 0 && @unconsumed_output.empty?
- end
-
- def process_one
- @in_process[Thread.current] = true
- begin
- begin
- input, index = @unconsumed_input.pop(true)
- process_input(input, index)
- rescue ThreadError
- end
- ensure
- @in_process.delete(Thread.current)
- end
- end
-
- def process_input(input, index)
- begin
- output = @block.call(input)
- @unconsumed_output.push([ output, index, input, :result ])
- rescue StandardError, ScriptError
- if @options[:stop_on_exception]
- @unconsumed_input.clear
- end
- @unconsumed_output.push([ $!, index, input, :exception ])
- end
-
- index
- end
- end
- end
- end
-end
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 094b59fc35..3a312701d2 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -858,11 +858,17 @@ class Chef
def profiling_prereqs!
require "ruby-prof"
- rescue LoadError
- raise "You must have the ruby-prof gem installed in order to use --profile-ruby"
+ rescue LoadError => e
+ raise "You must have the ruby-prof gem installed in order to use --profile-ruby: #{e.message}"
end
def start_profiling
+ if Chef::Config[:slow_report]
+ require_relative "handler/slow_report"
+
+ Chef::Config.report_handlers << Chef::Handler::SlowReport.new(Chef::Config[:slow_report])
+ end
+
return unless Chef::Config[:profile_ruby]
profiling_prereqs!
diff --git a/lib/chef/compliance/default_attributes.rb b/lib/chef/compliance/default_attributes.rb
index eb50c3a5e9..9196944b92 100644
--- a/lib/chef/compliance/default_attributes.rb
+++ b/lib/chef/compliance/default_attributes.rb
@@ -1,5 +1,5 @@
# Author:: Stephan Renatus <srenatus@chef.io>
-# Copyright:: (c) 2016-2019, Chef Software Inc. <legal@chef.io>
+# Copyright:: Copyright (c) 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.
@@ -27,8 +27,8 @@ class Chef
# 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",
+ # Accepted values: 'chef-server-automate', 'chef-automate', 'json-file', 'audit-enforcer', 'cli'
+ "reporter" => %w{json-file cli},
# 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.
@@ -38,11 +38,12 @@ class Chef
# Allow for connections to HTTPS endpoints using self-signed ssl certificates.
"insecure" => nil,
- # Controls verbosity of Chef InSpec runner.
+ # Controls verbosity of Chef InSpec runner. See less output when true.
"quiet" => true,
# Chef Inspec Compliance profiles to be used for scan of node.
- # See README.md for details
+ # See Compliance Phase documentation for further details:
+ # https://docs.chef.io/chef_compliance_phase/#compliance-phase-configuration
"profiles" => {},
# Extra inputs passed to Chef InSpec to allow finer-grained control over behavior.
@@ -83,7 +84,15 @@ class Chef
# 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
+ "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,
+
+ # Should the built-in compliance phase run. True and false force the behavior. Nil does magic based on if you have
+ # profiles defined but do not have the audit cookbook enabled.
+ "compliance_phase" => false
)
end
end
diff --git a/lib/chef/compliance/fetcher/automate.rb b/lib/chef/compliance/fetcher/automate.rb
index c86da331c2..47dc7f2e6a 100644
--- a/lib/chef/compliance/fetcher/automate.rb
+++ b/lib/chef/compliance/fetcher/automate.rb
@@ -7,8 +7,8 @@ class Chef
class Automate < ::InspecPlugins::Compliance::Fetcher
name "chef-automate"
- # it positions itself before `compliance` fetcher
- # only load it, if you want to use audit cookbook in Chef Solo with 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 = {
@@ -32,12 +32,12 @@ class Chef
profile_fetch_url = target[:url]
else
# verifies that the target e.g base/ssh exists
- base_path = "/compliance/profiles/#{uri.host}#{uri.path}"
-
+ profile = sanitize_profile_name(uri)
+ owner, id = profile.split("/")
profile_path = if target.respond_to?(:key?) && target.key?(:version)
- "#{base_path}/version/#{target[:version]}/tar"
+ "/compliance/profiles/#{owner}/#{id}/version/#{target[:version]}/tar"
else
- "#{base_path}/tar"
+ "/compliance/profiles/#{owner}/#{id}/tar"
end
url = URI(Chef::Config[:data_collector][:server_url])
@@ -46,13 +46,6 @@ class Chef
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)
@@ -60,6 +53,17 @@ class Chef
nil
end
+ # returns a parsed url for `admin/profile` or `compliance://admin/profile`
+ # TODO: remove in future, copied from inspec to support older versions of inspec
+ def self.sanitize_profile_name(profile)
+ uri = if URI(profile).scheme == "compliance"
+ URI(profile)
+ else
+ URI("compliance://#{profile}")
+ end
+ uri.to_s.sub(%r{^compliance:\/\/}, "")
+ end
+
def to_s
"#{ChefUtils::Dist::Automate::PRODUCT} for #{ChefUtils::Dist::Solo::PRODUCT} Fetcher"
end
diff --git a/lib/chef/compliance/fetcher/chef_server.rb b/lib/chef/compliance/fetcher/chef_server.rb
index db56aa82a7..96a2213b69 100644
--- a/lib/chef/compliance/fetcher/chef_server.rb
+++ b/lib/chef/compliance/fetcher/chef_server.rb
@@ -95,11 +95,11 @@ class Chef
def handle_http_error_code(code)
case code
when /401|403/
- Chef::Log.error "Auth issue: see audit cookbook TROUBLESHOOTING.md"
+ 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 audit cookbook TROUBLESHOOTING.md OR https://docs.chef.io/config_rb_server.html"
+ 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
diff --git a/lib/chef/compliance/reporter/automate.rb b/lib/chef/compliance/reporter/automate.rb
index ed5c4837d0..c40631771d 100644
--- a/lib/chef/compliance/reporter/automate.rb
+++ b/lib/chef/compliance/reporter/automate.rb
@@ -28,19 +28,28 @@ class Chef
@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
+ def validate_config!
+ unless @entity_uuid
+ # TODO - this is a weird leakage of naming from the parent class
+ # but entity_uuid is never an attribute that the user can see;
+ # it is sourced from chef_guid, which we don't technically know about in this class -
+ # but telling the operator about a missing chef_guid is more helpful than telling
+ # them about a missing field they've never heard of. Better would be a dock link
+ # that described how to fix this situation.
+ raise "CMPL004: automate_reporter: chef_guid is not available and must be provided. Aborting because we cannot report the scan."
+ end
+
+ unless @run_id
+ raise "CMPL005: automate_reporter: run_id is not available, aborting because we cannot report the scan."
end
unless @url && @token
- Chef::Log.warn "data_collector.token and data_collector.server_url must be defined in client.rb!"
- Chef::Log.warn "Further information: https://github.com/chef-cookbooks/audit#direct-reporting-to-chef-automate"
- return false
+ raise "CMPL006: data_collector.token and data_collector.server_url must be configured in client.rb! Further information: https://docs.chef.io/automate/data_collection/#configure-your-chef-infra-client-to-send-data-to-chef-automate-without-chef-infra-server"
end
+ end
+ # Method used in order to send the inspec report to the data_collector server
+ def send_report(report)
headers = {
"Content-Type" => "application/json",
"x-data-collector-auth" => "version=1.0",
diff --git a/lib/chef/compliance/reporter/chef_server_automate.rb b/lib/chef/compliance/reporter/chef_server_automate.rb
index be59a4cf69..569fb22426 100644
--- a/lib/chef/compliance/reporter/chef_server_automate.rb
+++ b/lib/chef/compliance/reporter/chef_server_automate.rb
@@ -7,6 +7,8 @@ class Chef
# 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]
@@ -28,11 +30,6 @@ class Chef
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
@@ -49,6 +46,16 @@ class Chef
false
end
+ def validate_config!
+ unless @entity_uuid
+ raise "CMPL007: chef_server_automate reporter: chef_guid is not available and must be provided. Aborting because we cannot report the scan"
+ end
+
+ unless @run_id
+ raise "CMPL008: chef_server_automate reporter: run_id is not available, aborting because we cannot report the scan."
+ end
+ end
+
def http_client
config = if @insecure
Chef::Config.merge(ssl_verify_mode: :verify_none)
@@ -74,11 +81,11 @@ class Chef
def handle_http_error_code(code)
case code
when /401|403/
- Chef::Log.error "Auth issue: see audit cookbook TROUBLESHOOTING.md"
+ 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 audit cookbook TROUBLESHOOTING.md OR https://docs.chef.io/config_rb_server.html"
+ Chef::Log.error "You most likely hit the request size limit 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
diff --git a/lib/chef/compliance/reporter/cli.rb b/lib/chef/compliance/reporter/cli.rb
new file mode 100644
index 0000000000..7061e5751c
--- /dev/null
+++ b/lib/chef/compliance/reporter/cli.rb
@@ -0,0 +1,77 @@
+class Chef
+ module Compliance
+ module Reporter
+ class Cli
+ def send_report(report)
+ # iterate over each profile and control
+ output = ["\nCompliance report:"]
+ report[:profiles].each do |profile|
+ next if profile[:controls].nil?
+
+ output << " * #{profile[:title]}"
+ profile[:controls].each do |control|
+ next if control[:results].nil?
+
+ output << "#{" " * 6}#{control[:title]}"
+ control[:results].each do |result|
+ output << format_result(result)
+ end
+ end
+ end
+ output << "\n"
+ puts output.join("\n")
+ end
+
+ def validate_config!
+ true
+ end
+
+ private
+
+ # pastel.decorate is a lightweight replacement for highline.color
+ def pastel
+ @pastel ||= begin
+ require "pastel" unless defined?(Pastel)
+ Pastel.new
+ end
+ end
+
+ def format_result(result)
+ output = []
+ found = false
+ if result[:status] == "failed"
+ if result[:code_desc]
+ found = true
+ output << pastel.red("#{" " * 9}- #{result[:code_desc]}")
+ end
+ if result[:message]
+ if found
+ result[:message].split(/\n/).reject(&:empty?).each do |m|
+ output << pastel.red("#{" " * 12}#{m}")
+ end
+ else
+ result[:message].split(/\n/).reject(&:empty?).each do |m|
+ output << pastel.red("#{" " * 9}#{m}")
+ end
+ end
+ found = true
+ end
+ unless found
+ output << pastel.red("#{" " * 9}- #{result[:status]}")
+ end
+ else
+ found = false
+ if result[:code_desc]
+ found = true
+ output << pastel.green("#{" " * 9}+ #{result[:code_desc]}")
+ end
+ unless found
+ output << pastel.green("#{" " * 9}+ #{result[:status]}")
+ end
+ end
+ output
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/reporter/compliance_enforcer.rb b/lib/chef/compliance/reporter/compliance_enforcer.rb
index 1c63e43b28..47b3a4d2df 100644
--- a/lib/chef/compliance/reporter/compliance_enforcer.rb
+++ b/lib/chef/compliance/reporter/compliance_enforcer.rb
@@ -14,6 +14,10 @@ class Chef
end
true
end
+
+ def validate_config!
+ true
+ end
end
end
end
diff --git a/lib/chef/compliance/reporter/json_file.rb b/lib/chef/compliance/reporter/json_file.rb
index 471d9f64b1..69091e3c7b 100644
--- a/lib/chef/compliance/reporter/json_file.rb
+++ b/lib/chef/compliance/reporter/json_file.rb
@@ -1,4 +1,5 @@
require_relative "../../json_compat"
+require_relative "../../log"
class Chef
module Compliance
@@ -9,10 +10,16 @@ class Chef
end
def send_report(report)
+ Chef::Log.info "Writing compliance report to #{@path}"
FileUtils.mkdir_p(File.dirname(@path), mode: 0700)
-
File.write(@path, Chef::JSONCompat.to_json(report))
end
+
+ def validate_config!
+ if @path.nil? || @path.class != String || @path.empty?
+ raise "CMPL009: json_file reporter: node['audit']['json_file']['location'] must contain a file path"
+ end
+ end
end
end
end
diff --git a/lib/chef/compliance/runner.rb b/lib/chef/compliance/runner.rb
index 677349df3e..ec68fe141a 100644
--- a/lib/chef/compliance/runner.rb
+++ b/lib/chef/compliance/runner.rb
@@ -1,27 +1,35 @@
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
+ SUPPORTED_REPORTERS = %w{chef-automate chef-server-automate json-file audit-enforcer cli}.freeze
+ SUPPORTED_FETCHERS = %w{chef-automate chef-server}.freeze
+
+ attr_accessor :run_id
attr_reader :node
def_delegators :node, :logger
def enabled?
- audit_cookbook_present = recipes.include?("audit::default")
+ return false if @node.nil?
+
+ # Did we parse the libraries file from the audit cookbook? This class dates back to when Chef Automate was
+ # renamed from Chef Visibility in 2017, so should capture all modern versions of the audit cookbook.
+ audit_cookbook_present = defined?(::Reporter::ChefAutomate)
- logger.info("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
- logger.info("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
+ logger.debug("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
+ logger.debug("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
+ logger.debug("#{self.class}##{__method__}: compliance phase attr? #{node["audit"]["compliance_phase"]}")
- inspec_profiles.any? && !audit_cookbook_present
+ if node["audit"]["compliance_phase"].nil?
+ inspec_profiles.any? && !audit_cookbook_present
+ else
+ node["audit"]["compliance_phase"]
+ end
end
def node=(node)
@@ -37,22 +45,30 @@ class Chef
self.run_id = run_status.run_id
end
- def run_list_expanded(run_list_expansion)
- self.recipes = run_list_expansion.recipes
+ def converge_start(run_context)
+ # With all attributes - including cookbook - loaded, we now have enough data to validate
+ # configuration. Because the converge is best coupled with the associated compliance run, these validations
+ # will raise (and abort the converge) if the compliance phase configuration is incorrect/will
+ # prevent compliance phase from completing and submitting its report to all configured reporters.
+ # can abort the converge if the compliance phase configuration (node attributes and client config)
+ load_and_validate!
end
def run_completed(_node, _run_status)
return unless enabled?
- logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
+ logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
report
end
def run_failed(_exception, _run_status)
- return unless enabled?
+ # If the run has failed because our own validation of compliance
+ # phase configuration has failed, we don't want to submit a report
+ # because we're still not configured correctly.
+ return unless enabled? && @validation_passed
- logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
+ logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
report
end
@@ -61,7 +77,6 @@ class Chef
DEPRECATED_CONFIG_VALUES = %w{
attributes_save
- chef_node_attribute_enabled
fail_if_not_present
inspec_gem_source
inspec_version
@@ -79,7 +94,11 @@ class Chef
end
end
- def report(report = generate_report)
+ def report(report = nil)
+ logger.info "Starting Chef Infra Compliance Phase"
+ report ||= generate_report
+ # This is invoked at report-time instead of with the normal validations at node loaded,
+ # because we want to ensure that it is visible in the output - and not lost in back-scroll.
warn_for_deprecated_config_values!
if report.empty?
@@ -87,15 +106,23 @@ class Chef
return
end
- Array(node["audit"]["reporter"]).each do |reporter|
- send_report(reporter, report)
+ Array(node["audit"]["reporter"]).each do |reporter_type|
+ logger.info "Reporting to #{reporter_type}"
+ @reporters[reporter_type].send_report(report)
end
+ logger.info "Chef Infra Compliance Phase Complete"
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: node["audit"]["attributes"],
+ inputs: inputs,
logger: logger,
output: node["audit"]["quiet"] ? ::File::NULL : STDOUT,
report: true,
@@ -108,10 +135,8 @@ class Chef
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."
+ raise "CMPL010: #{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
end
profiles.map do |name, profile|
@@ -127,8 +152,6 @@ class Chef
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
@@ -178,6 +201,8 @@ class Chef
# 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)
{
@@ -199,51 +224,83 @@ class Chef
}
end
- def send_report(reporter, report)
- logger.info "Reporting to #{reporter}"
-
- insecure = node["audit"]["insecure"]
- run_time_limit = node["audit"]["run_time_limit"]
- control_results_limit = node["audit"]["control_results_limit"]
-
- case reporter
+ def reporter(reporter_type)
+ case reporter_type
when "chef-automate"
+ require_relative "reporter/automate"
opts = {
+ control_results_limit: node["audit"]["control_results_limit"],
entity_uuid: node["chef_guid"],
- run_id: run_id,
+ insecure: node["audit"]["insecure"],
node_info: node_info,
- insecure: insecure,
- run_time_limit: run_time_limit,
- control_results_limit: control_results_limit,
+ run_id: run_id,
+ run_time_limit: node["audit"]["run_time_limit"],
}
- Chef::Compliance::Reporter::Automate.new(opts).send_report(report)
+ Chef::Compliance::Reporter::Automate.new(opts)
when "chef-server-automate"
- chef_url = node["audit"]["server"] || base_chef_server_url
- chef_org = Chef::Config[:chef_server_url].split("/").last
- if chef_url
- url = construct_url(chef_url, File.join("organizations", chef_org, "data-collector"))
- opts = {
- entity_uuid: node["chef_guid"],
- run_id: run_id,
- node_info: node_info,
- insecure: insecure,
- url: url,
- run_time_limit: run_time_limit,
- control_results_limit: control_results_limit,
- }
- Chef::Compliance::Reporter::ChefServer.new(opts).send_report(report)
- else
- logger.warn "Unable to determine #{ChefUtils::Dist::Server::PRODUCT} url required by #{Inspec::Dist::PRODUCT_NAME} report collector '#{reporter}'. Skipping..."
- end
+ require_relative "reporter/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).send_report(report)
+ require_relative "reporter/json_file"
+ path = node.dig("audit", "json_file", "location")
+ Chef::Compliance::Reporter::JsonFile.new(file: path)
when "audit-enforcer"
- Chef::Compliance::Reporter::ComplianceEnforcer.new.send_report(report)
- else
- logger.warn "#{reporter} is not a supported #{Inspec::Dist::PRODUCT_NAME} report collector"
+ require_relative "reporter/compliance_enforcer"
+ Chef::Compliance::Reporter::ComplianceEnforcer.new
+ when "cli"
+ require_relative "reporter/cli"
+ Chef::Compliance::Reporter::Cli.new
+ 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
+
+ # Load the resources required for this runner, and validate configuration
+ # is correct to proceed. Requires node state to be loaded.
+ # Will raise exception if fetcher is not valid, if a reporter is not valid,
+ # or the configuration required by a reporter is not provided.
+ def load_and_validate!
+ return unless enabled?
+
+ @reporters = {}
+ # Note that the docs don't say you can use an array, but our implementation
+ # supports it.
+ Array(node["audit"]["reporter"]).each do |type|
+ unless SUPPORTED_REPORTERS.include? type
+ raise "CMPL003: '#{type}' found in node['audit']['reporter'] is not a supported reporter for Compliance Phase. Supported reporters are: #{SUPPORTED_REPORTERS.join(", ")}. For more information, see the documentation at https://docs.chef.io/chef_compliance_phase#reporters"
+ end
+
+ @reporters[type] = reporter(type)
+ @reporters[type].validate_config!
+ end
+
+ unless (fetcher = node["audit"]["fetcher"]).nil?
+ unless SUPPORTED_FETCHERS.include? fetcher
+ raise "CMPL002: Unrecognized Compliance Phase fetcher (node['audit']['fetcher'] = #{fetcher}). Supported fetchers are: #{SUPPORTED_FETCHERS.join(", ")}, or nil. For more information, see the documentation at https://docs.chef.io/chef_compliance_phase#fetch-profiles"
+ end
end
+ @validation_passed = true
end
end
end
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index faed509321..00cf22e6da 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -160,13 +160,13 @@ class Chef
def metadata_filenames
return @metadata_filenames unless @metadata_filenames.empty?
- if File.exists?(File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE))
+ if File.exist?(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"))
+ if File.exist?(File.join(cookbook_path, "metadata.json"))
@metadata_filenames << File.join(cookbook_path, "metadata.json")
- elsif File.exists?(File.join(cookbook_path, "metadata.rb"))
+ elsif File.exist?(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
diff --git a/lib/chef/cookbook/gem_installer.rb b/lib/chef/cookbook/gem_installer.rb
index d7c18627de..e892212e32 100644
--- a/lib/chef/cookbook/gem_installer.rb
+++ b/lib/chef/cookbook/gem_installer.rb
@@ -70,7 +70,11 @@ class Chef
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 })
+ env = {
+ "PATH" => path_with_prepended_ruby_bin,
+ "BUNDLE_SILENCE_ROOT_WARNING" => "1",
+ }
+ so = shell_out!(cmd, cwd: dir, env: env)
Chef::Log.info(so.stdout)
end
end
diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb
index 53e874d0e8..e0ce5d7f41 100644
--- a/lib/chef/cookbook/synchronizer.rb
+++ b/lib/chef/cookbook/synchronizer.rb
@@ -143,11 +143,9 @@ class Chef
end
def files_remaining_by_cookbook
- @files_remaining_by_cookbook ||= begin
- files_by_cookbook.inject({}) do |memo, (cookbook, files)|
- memo[cookbook] = files.size
- memo
- end
+ @files_remaining_by_cookbook ||= files_by_cookbook.inject({}) do |memo, (cookbook, files)|
+ memo[cookbook] = files.size
+ memo
end
end
diff --git a/lib/chef/cookbook_loader.rb b/lib/chef/cookbook_loader.rb
index f7efb2646e..c1d35f7d7e 100644
--- a/lib/chef/cookbook_loader.rb
+++ b/lib/chef/cookbook_loader.rb
@@ -195,10 +195,8 @@ class Chef
def all_files_in_repo_paths
@all_files_in_repo_paths ||=
- begin
- repo_paths.inject([]) do |all_children, repo_path|
- all_children + Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")]
- end
+ repo_paths.inject([]) do |all_children, repo_path|
+ all_children + Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")]
end
end
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
deleted file mode 100644
index d7226b79b3..0000000000
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ /dev/null
@@ -1,244 +0,0 @@
-#
-# Author:: Stanislav Vitvitskiy
-# Author:: Nuo Yan (nuo@chef.io)
-# Author:: Christopher Walters (<cw@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.
-#
-
-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
- # A streaming multipart HTTP upload implementation. Used to upload cookbooks
- # (in tarball form) to https://supermarket.chef.io
- #
- # inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
- class CookbookSiteStreamingUploader
-
- 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("#{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.trace("Staging at #{tmp_cookbook_dir}")
- 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_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.trace("Generating metadata")
- kcm = Chef::Knife::CookbookMetadata.new
- kcm.config[:cookbook_path] = [ tmp_cookbook_dir ]
- kcm.name_args = [ cookbook.name.to_s ]
- kcm.run
-
- tmp_cookbook_dir
- end
-
- def post(to_url, user_id, secret_key_filename, params = {}, headers = {})
- make_request(:post, to_url, user_id, secret_key_filename, params, headers)
- end
-
- def put(to_url, user_id, secret_key_filename, params = {}, headers = {})
- make_request(:put, to_url, user_id, secret_key_filename, params, headers)
- end
-
- def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {})
- boundary = "----RubyMultipartClient" + rand(1000000).to_s + "ZZZZZ"
- parts = []
- content_file = nil
-
- secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename))
-
- unless params.nil? || params.empty?
- params.each do |key, value|
- if value.is_a?(File)
- content_file = value
- filepath = value.path
- filename = File.basename(filepath)
- parts << StringPart.new( "--" + boundary + "\r\n" +
- "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" +
- "Content-Type: application/octet-stream\r\n\r\n")
- parts << StreamPart.new(value, File.size(filepath))
- parts << StringPart.new("\r\n")
- else
- parts << StringPart.new( "--" + boundary + "\r\n" +
- "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n")
- parts << StringPart.new(value.to_s + "\r\n")
- end
- end
- parts << StringPart.new("--" + boundary + "--\r\n")
- end
-
- body_stream = MultipartStream.new(parts)
-
- timestamp = Time.now.utc.iso8601
-
- url = URI.parse(to_url)
-
- Chef::Log.logger.debug("Signing: method: #{http_verb}, url: #{url}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}")
-
- # We use the body for signing the request if the file parameter
- # wasn't a valid file or wasn't included. Extract the body (with
- # multi-part delimiters intact) to sign the request.
- # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and
- # always hash the entire request body. In the file case it would just be
- # expanded multipart text - the entire body of the POST.
- content_body = parts.inject("") { |result, part| result + part.read(0, part.size) }
- 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 }
- (content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || ""))
-
- headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key))
-
- content_file.rewind if content_file
-
- # net/http doesn't like symbols for header keys, so we'll to_s each one just in case
- headers = DefaultHeaders.merge(Hash[*headers.map { |k, v| [k.to_s, v] }.flatten])
-
- req = case http_verb
- when :put
- Net::HTTP::Put.new(url.path, headers)
- when :post
- Net::HTTP::Post.new(url.path, headers)
- end
- req.content_length = body_stream.size
- req.content_type = "multipart/form-data; boundary=" + boundary unless parts.empty?
- req.body_stream = body_stream
-
- http = Chef::HTTP::BasicClient.new(url).http_client
- res = http.request(req)
-
- # alias status to code and to_s to body for test purposes
- # TODO: stop the following madness!
- class << res
- alias :to_s :body
-
- # 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
-
- def status # rubocop:disable Lint/NestedMethodDefinition
- code.to_i
- end
- end
- res
- end
-
- end
-
- class StreamPart
- def initialize(stream, size)
- @stream, @size = stream, size
- end
-
- def size
- @size
- end
-
- # read the specified amount from the stream
- def read(offset, how_much)
- @stream.read(how_much)
- end
- end
-
- class StringPart
- def initialize(str)
- @str = str
- end
-
- def size
- @str.length
- end
-
- # read the specified amount from the string starting at the offset
- def read(offset, how_much)
- @str[offset, how_much]
- end
- end
-
- class MultipartStream
- def initialize(parts)
- @parts = parts
- @part_no = 0
- @part_offset = 0
- end
-
- def size
- @parts.inject(0) { |size, part| size + part.size }
- end
-
- def read(how_much, dst_buf = nil)
- if @part_no >= @parts.size
- dst_buf.replace("") if dst_buf
- return dst_buf
- end
-
- how_much_current_part = @parts[@part_no].size - @part_offset
-
- how_much_current_part = if how_much_current_part > how_much
- how_much
- else
- how_much_current_part
- end
-
- how_much_next_part = how_much - how_much_current_part
-
- current_part = @parts[@part_no].read(@part_offset, how_much_current_part)
-
- # recurse into the next part if the current one was not large enough
- if how_much_next_part > 0
- @part_no += 1
- @part_offset = 0
- next_part = read(how_much_next_part)
- result = current_part + (next_part || "")
- else
- @part_offset += how_much_current_part
- result = current_part
- end
- dst_buf ? dst_buf.replace(result || "") : result
- end
- end
-
- end
-end
diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb
index 235a539b94..21a15f706c 100644
--- a/lib/chef/cookbook_uploader.rb
+++ b/lib/chef/cookbook_uploader.rb
@@ -1,7 +1,6 @@
autoload :Set, "set"
require_relative "exceptions"
-require_relative "knife/cookbook_metadata"
require_relative "digester"
require_relative "cookbook_manifest"
require_relative "cookbook_version"
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 420532585a..6e4f13c291 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -138,11 +138,14 @@ class Chef
end
def recipe_yml_filenames_by_name
- @recipe_ym_filenames_by_name ||= begin
+ @recipe_yml_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" }
+ root_alias = cookbook_manifest.root_files.find { |record|
+ record[:name] == "root_files/recipe.yml" ||
+ record[:name] == "root_files/recipe.yaml"
+ }
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"]
+ Chef::Log.error("Cookbook #{name} contains both recipe.yml and recipes/default.yml, ignoring recipes/default.yml") if name_map["default"]
name_map["default"] = root_alias[:full_path]
end
name_map
@@ -582,8 +585,27 @@ class Chef
records.select { |record| record[:name] =~ /\.rb$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".rb")] = record[:full_path]; memo }
end
+ # Filters YAML files from the superset of provided files.
+ # Checks for duplicate basenames with differing extensions (eg yaml v yml)
+ # and raises error if any are detected.
+ # This prevents us from arbitrarily the ".yaml" or ".yml" version when both are present,
+ # because we don't know which is correct.
+ # This method runs in O(n^2) where N = number of yml files present. This number should be consistently
+ # low enough that there's no noticeable perf impact.
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 }
+ yml_files = records.select { |record| record[:name] =~ /\.(y[a]?ml)$/ }
+ result = yml_files.inject({}) do |acc, record|
+ filename = record[:name]
+ base_dup_name = File.join(File.dirname(filename), File.basename(filename, File.extname(filename)))
+ yml_files.each do |other|
+ if other[:name] =~ /#{(File.extname(filename) == ".yml") ? "#{base_dup_name}.yaml" : "#{base_dup_name}.yml"}$/
+ raise Chef::Exceptions::AmbiguousYAMLFile.new("Cookbook #{name}@#{version} contains ambiguous files: #{filename} and #{other[:name]}. Please update the cookbook to remove the incorrect file.")
+ end
+ end
+ acc[File.basename(record[:name], File.extname(record[:name]))] = record[:full_path]
+ acc
+ end
+ result
end
def file_vendor
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index af0560a640..26bf49a008 100644
--- a/lib/chef/data_bag.rb
+++ b/lib/chef/data_bag.rb
@@ -32,7 +32,8 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- VALID_NAME = /^[\.\-[:alnum:]_]+$/.freeze
+ # Regex reference: https://rubular.com/r/oIMySIO4USPm5x
+ VALID_NAME = /^[\-[:alnum:]_]+$/.freeze
RESERVED_NAMES = /^(node|role|environment|client)$/.freeze
def self.validate_name!(name)
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 94cf2a5317..17a105a84e 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -36,7 +36,8 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- VALID_ID = /^[\.\-[:alnum:]_]+$/.freeze
+ # Regex reference: https://rubular.com/r/oIMySIO4USPm5x
+ VALID_ID = /^[\-[:alnum:]_]+$/.freeze
def self.validate_id!(id_str)
if id_str.nil? || ( id_str !~ VALID_ID )
@@ -44,8 +45,17 @@ class Chef
end
end
- # Define all Hash's instance methods as delegating to @raw_data
- def_delegators(:@raw_data, *(Hash.instance_methods - Object.instance_methods))
+ # delegate missing methods to the @raw_data Hash
+ def method_missing(method_name, *arguments, &block)
+ @raw_data.send(method_name, *arguments, &block)
+ rescue
+ # throw more sensible errors back at the user
+ super
+ end
+
+ def respond_to_missing?(method_name, include_private = false)
+ @raw_data.respond_to?(method_name, include_private) || super
+ end
attr_reader :raw_data
diff --git a/lib/chef/data_collector.rb b/lib/chef/data_collector.rb
index 8d76f8a7b2..0e621f1492 100644
--- a/lib/chef/data_collector.rb
+++ b/lib/chef/data_collector.rb
@@ -104,7 +104,6 @@ class Chef
#
def action_collection_registration(action_collection)
@action_collection = action_collection
- action_collection.register(self)
end
# - Creates and writes our NodeUUID back to the node object
diff --git a/lib/chef/data_collector/run_end_message.rb b/lib/chef/data_collector/run_end_message.rb
index 1900effa26..91cf21e643 100644
--- a/lib/chef/data_collector/run_end_message.rb
+++ b/lib/chef/data_collector/run_end_message.rb
@@ -51,7 +51,7 @@ class Chef
"id" => run_status&.run_id,
"message_version" => "1.1.0",
"message_type" => "run_converge",
- "node" => node || {},
+ "node" => node&.data_for_save || {},
"node_name" => node&.name || data_collector.node_name,
"organization_name" => organization,
"resources" => all_action_records(action_collection),
diff --git a/lib/chef/delayed_evaluator.rb b/lib/chef/delayed_evaluator.rb
index df734c8303..f200f88410 100644
--- a/lib/chef/delayed_evaluator.rb
+++ b/lib/chef/delayed_evaluator.rb
@@ -17,5 +17,9 @@
class Chef
class DelayedEvaluator < Proc
+ def dup
+ # super returns a "Proc" (which seems buggy) so re-wrap it
+ self.class.new(&super) # rubocop:disable Layout/SpaceAroundKeyword
+ end
end
end
diff --git a/lib/chef/deprecated.rb b/lib/chef/deprecated.rb
index 992876c17d..ae205497c4 100644
--- a/lib/chef/deprecated.rb
+++ b/lib/chef/deprecated.rb
@@ -249,6 +249,14 @@ class Chef
target 32
end
+ class UnifiedMode < Base
+ target 33
+ end
+
+ class AttributeWhitelistConfiguration < Base
+ target 34
+ end
+
class Generic < Base
def url
"https://docs.chef.io/chef_deprecations_client/"
diff --git a/lib/chef/dsl/chef_vault.rb b/lib/chef/dsl/chef_vault.rb
index 031627c358..3411f79e41 100644
--- a/lib/chef/dsl/chef_vault.rb
+++ b/lib/chef/dsl/chef_vault.rb
@@ -32,8 +32,8 @@ class Chef
# 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']
+ # 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)
@@ -51,8 +51,8 @@ class Chef
# 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'
+ # 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)
@@ -68,8 +68,8 @@ class Chef
# 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'
+ # 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]
diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index 8053098085..df0ee0b719 100644
--- a/lib/chef/dsl/declare_resource.rb
+++ b/lib/chef/dsl/declare_resource.rb
@@ -156,15 +156,7 @@ class Chef
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
- 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
+ declare_resource(type, name, created_at: created_at, run_context: run_context, &resource_attrs_block)
end
# Find existing resources by searching the list of existing resources. Possible
@@ -306,6 +298,8 @@ class Chef
enclosing_provider ||= self if is_a?(Chef::Provider)
+ nr = new_resource if defined?(new_resource)
+
Chef::ResourceBuilder.new(
type: type,
name: name,
@@ -314,7 +308,8 @@ class Chef
run_context: run_context,
cookbook_name: cookbook_name,
recipe_name: recipe_name,
- enclosing_provider: enclosing_provider
+ enclosing_provider: enclosing_provider,
+ new_resource: nr
).build(&resource_attrs_block)
end
diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb
index 19d3a6b0bd..e115d5441d 100644
--- a/lib/chef/dsl/reboot_pending.rb
+++ b/lib/chef/dsl/reboot_pending.rb
@@ -45,9 +45,8 @@ class Chef
# 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")
+ elsif platform_family?("debian")
+ File.exist?("/var/run/reboot-required")
else
false
end
diff --git a/lib/chef/encrypted_data_bag_item/assertions.rb b/lib/chef/encrypted_data_bag_item/assertions.rb
index 02baad2a2d..13ed0de050 100644
--- a/lib/chef/encrypted_data_bag_item/assertions.rb
+++ b/lib/chef/encrypted_data_bag_item/assertions.rb
@@ -30,7 +30,7 @@ class Chef::EncryptedDataBagItem
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/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index f7b706cb2c..f9504967a9 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -221,7 +221,8 @@ class Chef
# Called before convergence starts
def converge_start(run_context); end
- # Callback hook for handlers to register their interest in the action_collection
+ # Callback hook for handlers to grab a reference to the action_collection
+ # (sent before compiling cookbooks, consumers can also find it off the run_context.action_collection)
def action_collection_registration(action_collection); end
# Called when the converge phase is finished.
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index a0afd25208..ba34b22e61 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -174,6 +174,9 @@ class Chef
class CannotDetermineWindowsInstallerType < Package; end
class NoWindowsPackageSource < Package; end
+ # for example, if both recipes/default.yml, recipes/default.yaml are present
+ class AmbiguousYAMLFile < RuntimeError; end
+
# Can not create staging file during file deployment
class FileContentStagingError < RuntimeError
def initialize(errors)
diff --git a/lib/chef/file_access_control/windows.rb b/lib/chef/file_access_control/windows.rb
index cc1b96a84d..744c5f7466 100644
--- a/lib/chef/file_access_control/windows.rb
+++ b/lib/chef/file_access_control/windows.rb
@@ -33,7 +33,7 @@ class Chef
module ClassMethods
# We want to mix these in as class methods
def writable?(path)
- ::File.exists?(path) && Chef::ReservedNames::Win32::File.file_access_check(
+ ::File.exist?(path) && Chef::ReservedNames::Win32::File.file_access_check(
path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE
)
end
@@ -136,7 +136,7 @@ class Chef
end
def should_update_dacl?
- return true unless ::File.exists?(file) || ::File.symlink?(file)
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
dacl = target_dacl
existing_dacl = existing_descriptor.dacl
@@ -170,7 +170,7 @@ class Chef
end
def should_update_group?
- return true unless ::File.exists?(file) || ::File.symlink?(file)
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
(group = target_group) && (group != existing_descriptor.group)
end
@@ -190,7 +190,7 @@ class Chef
end
def should_update_owner?
- return true unless ::File.exists?(file) || ::File.symlink?(file)
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
(owner = target_owner) && (owner != existing_descriptor.owner)
end
diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb
index 22060869da..851234596a 100644
--- a/lib/chef/file_cache.rb
+++ b/lib/chef/file_cache.rb
@@ -79,7 +79,7 @@ class Chef
file_path_array = File.split(path)
file_name = file_path_array.pop
- if File.exists?(file) && File.writable?(file)
+ if File.exist?(file) && File.writable?(file)
FileUtils.mv(
file,
File.join(create_cache_path(File.join(file_path_array), true), file_name)
@@ -112,7 +112,7 @@ class Chef
}
)
cache_path = create_cache_path(path, false)
- raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path)
+ raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exist?(cache_path)
if read
File.read(cache_path)
@@ -139,7 +139,7 @@ class Chef
}
)
cache_path = create_cache_path(path, false)
- if File.exists?(cache_path)
+ if File.exist?(cache_path)
File.unlink(cache_path)
end
true
@@ -186,7 +186,7 @@ class Chef
}
)
full_path = create_cache_path(path, false)
- if File.exists?(full_path)
+ if File.exist?(full_path)
true
else
false
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index 513ac45471..2a3f9faef3 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -56,7 +56,8 @@ class Chef
# Print out deprecations.
unless deprecations.empty?
puts_line ""
- puts_line "Deprecated features used!"
+ puts_line "Deprecation warnings that must be addressed before upgrading to Chef Infra #{Chef::VERSION.to_i + 1}:"
+ puts_line ""
deprecations.each do |message, details|
locations = details[:locations]
if locations.size == 1
diff --git a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
index 905a438f56..4010bd72d5 100644
--- a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
@@ -64,36 +64,34 @@ class Chef
def recipe_snippet
return nil if dynamic_resource?
- @snippet ||= begin
- if (file = parse_source) && (line = parse_line(file))
- return nil unless ::File.exists?(file)
+ @snippet ||= if (file = parse_source) && (line = parse_line(file))
+ return nil unless ::File.exist?(file)
- lines = IO.readlines(file)
+ lines = IO.readlines(file)
- relevant_lines = ["# In #{file}\n\n"]
+ relevant_lines = ["# In #{file}\n\n"]
- current_line = line - 1
- current_line = 0 if current_line < 0
- nesting = 0
+ current_line = line - 1
+ current_line = 0 if current_line < 0
+ nesting = 0
- loop do
+ loop do
- # low rent parser. try to gracefully handle nested blocks in resources
- nesting += 1 if /\s+do\s*/.match?(lines[current_line])
- nesting -= 1 if /end\s*$/.match?(lines[current_line])
+ # low rent parser. try to gracefully handle nested blocks in resources
+ 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])
+ relevant_lines << format_line(current_line, lines[current_line])
- break if lines[current_line + 1].nil?
- break if current_line >= (line + 50)
- break if nesting <= 0
+ break if lines[current_line + 1].nil?
+ break if current_line >= (line + 50)
+ break if nesting <= 0
- current_line += 1
- end
- relevant_lines << format_line(current_line + 1, lines[current_line + 1]) if lines[current_line + 1]
- relevant_lines.join("")
- end
- end
+ current_line += 1
+ end
+ relevant_lines << format_line(current_line + 1, lines[current_line + 1]) if lines[current_line + 1]
+ relevant_lines.join("")
+ end
end
def dynamic_resource?
diff --git a/lib/chef/group.rb b/lib/chef/group.rb
new file mode 100644
index 0000000000..72f5b7b474
--- /dev/null
+++ b/lib/chef/group.rb
@@ -0,0 +1,75 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "org"
+
+class Chef
+ class Group
+
+ def group(groupname)
+ @group ||= {}
+ @group[groupname] ||= chef_rest.get_rest "organizations/#{name}/groups/#{groupname}"
+ end
+
+ def user_member_of_group?(username, groupname)
+ group = group(groupname)
+ group["actors"].include? username
+ end
+
+ def add_user_to_group(groupname, username)
+ group = group(groupname)
+ body_hash = {
+ groupname: "#{groupname}",
+ actors: {
+ "users" => group["actors"].concat([username]),
+ "groups" => group["groups"],
+ },
+ }
+ chef_rest.put_rest "organizations/#{name}/groups/#{groupname}", body_hash
+ end
+
+ def remove_user_from_group(groupname, username)
+ group = group(groupname)
+ group["actors"].delete(username)
+ body_hash = {
+ groupname: "#{groupname}",
+ actors: {
+ "users" => group["actors"],
+ "groups" => group["groups"],
+ },
+ }
+ chef_rest.put_rest "organizations/#{name}/groups/#{groupname}", body_hash
+ end
+
+ def actor_delete_would_leave_admins_empty?
+ admins = group("admins")
+ if admins["groups"].empty?
+ # exclude 'pivotal' but don't mutate the group since we're caching it
+ if admins["actors"].include? "pivotal"
+ admins["actors"].length <= 2
+ else
+ admins["actors"].length <= 1
+ end
+ else
+ # We don't check recursively. If the admins group contains a group,
+ # and the user is the only member of that group,
+ # we'll still turn up a 'safe to delete'.
+ false
+ end
+ end
+ end
+end
diff --git a/lib/chef/handler.rb b/lib/chef/handler.rb
index 97562ea31b..173d4c4faa 100644
--- a/lib/chef/handler.rb
+++ b/lib/chef/handler.rb
@@ -55,6 +55,12 @@ class Chef
#
class Handler
+ # FIXME: Chef::Handler should probably inherit from EventDispatch::Base
+ # and should wire up to those events rather than the "notifications" system
+ # which is hanging off of Chef::Client. Those "notifications" could then be
+ # deprecated in favor of events, and this class could become decoupled from
+ # the Chef::Client object.
+
def self.handler_for(*args)
if args.include?(:start)
Chef::Config[:start_handlers] ||= []
@@ -207,17 +213,45 @@ class Chef
# The Chef::Node for this client run
def_delegator :@run_status, :node
- ##
- # :method: all_resources
+ # @return Array<Chef::Resource> all resources other than unprocessed
#
- # An Array containing all resources in the chef run's resource_collection
- def_delegator :@run_status, :all_resources
+ def all_resources
+ @all_resources ||= action_collection&.filtered_collection(unprocessed: false)&.resources || []
+ end
- ##
- # :method: updated_resources
+ # @return Array<Chef::Resource> all updated resources
+ #
+ def updated_resources
+ @updated_resources ||= action_collection&.filtered_collection(up_to_date: false, skipped: false, failed: false, unprocessed: false)&.resources || []
+ end
+
+ # @return Array<Chef::Resource> all up_to_date resources
+ #
+ def up_to_date_resources
+ @up_to_date_resources ||= action_collection&.filtered_collection(updated: false, skipped: false, failed: false, unprocessed: false)&.resources || []
+ end
+
+ # @return Array<Chef::Resource> all failed resources
#
- # An Array containing all resources that were updated during the chef run
- def_delegator :@run_status, :updated_resources
+ def failed_resources
+ @failed_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, skipped: false, unprocessed: false)&.resources || []
+ end
+
+ # @return Array<Chef::Resource> all skipped resources
+ #
+ def skipped_resources
+ @skipped_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, failed: false, unprocessed: false)&.resources || []
+ end
+
+ # Unprocessed resources are those which are left over in the outer recipe context when a run fails.
+ # Sub-resources of unprocessed resourced are impossible to capture because they would require processing
+ # the outer resource.
+ #
+ # @return Array<Chef::Resource> all unprocessed resources
+ #
+ def unprocessed_resources
+ @unprocessed_resources ||= action_collection&.filtered_collection(updated: false, up_to_date: false, failed: false, skipped: false)&.resources || []
+ end
##
# :method: success?
@@ -232,6 +266,10 @@ class Chef
# Did the chef run fail? True if the chef run raised an uncaught exception
def_delegator :@run_status, :failed?
+ def action_collection
+ @run_status.run_context.action_collection
+ end
+
# The main entry point for report handling. Subclasses should override this
# method with their own report handling logic.
def report; end
diff --git a/lib/chef/handler/json_file.rb b/lib/chef/handler/json_file.rb
index 6318c30d74..998f2b0e0e 100644
--- a/lib/chef/handler/json_file.rb
+++ b/lib/chef/handler/json_file.rb
@@ -51,7 +51,7 @@ class Chef
end
def build_report_dir
- unless File.exists?(config[:path])
+ unless File.exist?(config[:path])
FileUtils.mkdir_p(config[:path])
File.chmod(00700, config[:path])
end
diff --git a/lib/chef/handler/slow_report.rb b/lib/chef/handler/slow_report.rb
new file mode 100644
index 0000000000..e5b2f2895f
--- /dev/null
+++ b/lib/chef/handler/slow_report.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 "../handler"
+require "tty/table" unless defined?(TTY::Table)
+
+class Chef
+ class Handler
+ class SlowReport < ::Chef::Handler
+ attr_accessor :amount
+
+ def initialize(amount)
+ @amount = Integer(amount) rescue nil
+ @amount ||= 10
+ end
+
+ def report
+ if count == 0
+ puts "\nNo resources to profile\n\n"
+ return
+ end
+
+ top = all_records.sort_by(&:elapsed_time).last(amount).reverse
+ data = top.map { |r| [ r.new_resource.to_s, r.elapsed_time, r.action, r.new_resource.cookbook_name, r.new_resource.recipe_name, stripped_source_line(r.new_resource) ] }
+ puts "\nTop #{count} slowest #{count == 1 ? "resource" : "resources"}:\n\n"
+ table = TTY::Table.new(%w{resource elapsed_time action cookbook recipe source}, data)
+ rendered = table.render do |renderer|
+ renderer.border do
+ mid "-"
+ mid_mid " "
+ end
+ end
+ puts rendered
+ puts "\n"
+ end
+
+ def all_records
+ @all_records ||= action_collection&.filtered_collection(unprocessed: false) || []
+ end
+
+ def count
+ num = all_resources.count
+ num > amount ? amount : num
+ end
+
+ def stripped_source_line(resource)
+ # strip the leading path off of the source line
+ resource.source_line&.gsub(%r{.*/cookbooks/}, "")&.gsub(%r{.*/chef-[0-9\.]+/}, "")
+ end
+ end
+ end
+end
diff --git a/lib/chef/http/ssl_policies.rb b/lib/chef/http/ssl_policies.rb
index d36e747ff6..bc688f13a6 100644
--- a/lib/chef/http/ssl_policies.rb
+++ b/lib/chef/http/ssl_policies.rb
@@ -85,28 +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
+ 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
- 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]))
+ 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
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
deleted file mode 100644
index ac7a68d0fc..0000000000
--- a/lib/chef/knife.rb
+++ /dev/null
@@ -1,665 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Brown (<cb@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 "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 = "#{ChefUtils::Dist::Infra::PRODUCT} Knife#{Chef::HTTP::HTTPRequest::UA_COMMON}"
-
- include Mixlib::CLI
- include ChefUtils::DSL::DefaultPaths
- extend Chef::Mixin::ConvertToClassName
- extend Forwardable
-
- # @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
- def_delegator :@ui, :msg
- def_delegator :@ui, :ask_question
- def_delegator :@ui, :pretty_print
- def_delegator :@ui, :output
- def_delegator :@ui, :format_list_for_display
- def_delegator :@ui, :format_for_display
- def_delegator :@ui, :format_cookbook_list_for_display
- def_delegator :@ui, :edit_data
- def_delegator :@ui, :edit_hash
- def_delegator :@ui, :edit_object
- def_delegator :@ui, :confirm
-
- 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
- end
-
- def self.ui
- @ui ||= Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
- end
-
- def self.msg(msg = "")
- ui.msg(msg)
- end
-
- def self.reset_config_loader!
- @@chef_config_dir = nil
- @config_loader = nil
- end
-
- def self.reset_subcommands!
- @@subcommands = {}
- @subcommands_by_category = nil
- 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
- # 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])]
- else
- [path_from_caller(caller[0])]
- end
- end
- end
-
- # 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
- # @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
-
- def self.subcommand_category
- @category || snake_case_name.split("_").first unless unnamed?
- end
-
- def self.snake_case_name
- convert_to_snake_case(name.split("::").last) unless unnamed?
- end
-
- def self.common_name
- snake_case_name.split("_").join(" ")
- end
-
- # Does this class have a name? (Classes created via Class.new don't)
- def self.unnamed?
- name.nil? || name.empty?
- end
-
- def self.subcommand_loader
- @subcommand_loader ||= Chef::Knife::SubcommandLoader.for_config(chef_config_dir)
- end
-
- def self.load_commands
- @commands_loaded ||= subcommand_loader.load_commands
- end
-
- def self.guess_category(args)
- subcommand_loader.guess_category(args)
- end
-
- def self.subcommand_class_from(args)
- 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)
- end
- subcommand_loader.command_class_from(args) || subcommand_not_found!(args)
- end
-
- def self.subcommands
- @@subcommands ||= {}
- end
-
- def self.subcommand_files
- @@subcommand_files ||= Hash.new([])
- end
-
- def self.subcommands_by_category
- unless @subcommands_by_category
- @subcommands_by_category = Hash.new { |hash, key| hash[key] = [] }
- subcommands.each do |snake_cased, klass|
- @subcommands_by_category[klass.subcommand_category] << snake_cased
- end
- end
- @subcommands_by_category
- end
-
- # Shared with subclasses
- @@chef_config_dir = nil
-
- def self.config_loader
- @config_loader ||= WorkstationConfigLoader.new(nil, Chef::Log)
- end
-
- 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. See https://docs.chef.io/config_rb/ for details.") if config_loader.no_config_found?
-
- config_loader
- rescue Exceptions::ConfigurationError => e
- ui.error(ui.color("CONFIGURATION ERROR:", :red) + e.message)
- exit 1
- end
-
- def self.chef_config_dir
- @@chef_config_dir ||= config_loader.chef_config_dir
- end
-
- # Run knife for the given +args+ (ARGV), adding +options+ to the list of
- # CLI options that the subcommand knows how to handle.
- #
- # @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
- # config file is read may be lost. If the KNIFE_DEBUG variable is set, we
- # setup the logger for debug logging to stderr immediately to catch info
- # from early in the setup process.
- if ENV["KNIFE_DEBUG"]
- Chef::Log.init($stderr)
- Chef::Log.level(:debug)
- end
-
- subcommand_class = subcommand_class_from(args)
- subcommand_class.options = options.merge!(subcommand_class.options)
- subcommand_class.load_deps
- instance = subcommand_class.new(args)
- instance.configure_chef
- instance.run_with_pretty_exceptions
- end
-
- def self.dependency_loaders
- @dependency_loaders ||= []
- end
-
- def self.deps(&block)
- dependency_loaders << block
- end
-
- def self.load_deps
- dependency_loaders.each(&:call)
- end
-
- 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
- def path_from_caller(caller_line)
- caller_line.split(/:\d+/).first
- end
-
- # 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(" ")}'")
-
- # Mention rehash when the subcommands cache(plugin_manifest.json) is used
- if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader)
- ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
- end
-
- if CHEF_ORGANIZATION_MANAGEMENT.include?(args[0])
- list_commands("CHEF ORGANIZATION MANAGEMENT")
- elsif category_commands = guess_category(args)
- list_commands(category_commands)
- 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
-
- exit 10
- end
-
- # @api private
- def reset_config_path!
- @@chef_config_dir = nil
- end
-
- end
-
- reset_config_path!
-
- # Create a new instance of the current class configured for the given
- # arguments and options
- def initialize(argv = [])
- super() # having to call super in initialize is the most annoying anti-pattern :(
- @ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, config)
-
- command_name_words = self.class.snake_case_name.split("_")
-
- # Mixlib::CLI ignores the embedded name_args
- @name_args = parse_options(argv)
- @name_args.delete(command_name_words.join("-"))
- @name_args.reject! { |name_arg| command_name_words.delete(name_arg) }
-
- # 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_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
-
- # 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
-
- def parse_options(args)
- super
- rescue OptionParser::InvalidOption => e
- puts "Error: " + e.to_s
- show_usage
- exit(1)
- end
-
- # 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
-
- # 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
- #
- def config_file_settings
- 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)
- # 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(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
- # components, such as expanding file paths and converting verbosity level
- # into log level.
- def apply_computed_config
- Chef::Config[:color] = config[:color]
-
- case Chef::Config[:verbosity]
- when 0, nil
- Chef::Config[:log_level] = :warn
- when 1
- Chef::Config[:log_level] = :info
- when 2
- Chef::Config[:log_level] = :debug
- else
- Chef::Config[:log_level] = :trace
- end
-
- 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.key?(:local_mode)
-
- Chef::Config.listen = config[:listen] if config.key?(:listen)
-
- 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]
- Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port]
-
- # Expand a relative path from the config directory. Config from command
- # line should already be expanded, and absolute paths will be unchanged.
- if Chef::Config[:client_key] && config[:config_file]
- Chef::Config[:client_key] = File.expand_path(Chef::Config[:client_key], File.dirname(config[:config_file]))
- end
-
- Mixlib::Log::Formatter.show_time = false
- Chef::Log.init(Chef::Config[:log_location])
- Chef::Log.level(Chef::Config[:log_level] || :error)
- end
-
- 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[: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
-
- # 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: " + opt_parser.to_s)
- end
-
- def run_with_pretty_exceptions(raise_exception = false)
- unless respond_to?(:run)
- ui.error "You need to add a #run method to your knife command before you can use it"
- end
- 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] && Chef::Config[:verbosity] >= 2 )
-
- humanize_exception(e)
- exit 100
- end
-
- def humanize_exception(e)
- case e
- when SystemExit
- raise # make sure exit passes through.
- 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 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}"
- when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError
- ui.error "Network Error: #{e.message}"
- ui.info "Check your knife configuration and network settings"
- 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 `-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 config.rb/knife.rb to the server's FQDN to avoid unwanted redirections."
- else
- ui.error "#{e.class.name}: #{e.message}"
- end
- end
-
- def humanize_http_exception(e)
- response = e.response
- case response
- when Net::HTTPUnauthorized
- 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."
- 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"
- ui.info "Response: #{format_rest_error(response)}"
- when Net::HTTPNotFound
- ui.error "The object you are looking for could not be found"
- ui.info "Response: #{format_rest_error(response)}"
- when Net::HTTPInternalServerError
- ui.error "internal server error"
- ui.info "Response: #{format_rest_error(response)}"
- when Net::HTTPBadGateway
- ui.error "bad gateway"
- ui.info "Response: #{format_rest_error(response)}"
- when Net::HTTPServiceUnavailable
- ui.error "Service temporarily unavailable"
- ui.info "Response: #{format_rest_error(response)}"
- when Net::HTTPNotAcceptable
- version_header = Chef::JSONCompat.from_json(response["x-ops-server-api-version"])
- client_api_version = version_header["request_version"]
- min_server_version = version_header["min_version"]
- max_server_version = version_header["max_version"]
- 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)}"
- end
- end
-
- def username
- Chef::Config[:node_name]
- end
-
- def api_key
- Chef::Config[:client_key]
- end
-
- # Parses JSON from the error response sent by Chef Server and returns the
- # error message
- #--
- # TODO: this code belongs in Chef::REST
- def format_rest_error(response)
- Array(Chef::JSONCompat.from_json(response.body)["error"]).join("; ")
- rescue Exception
- response.body
- end
-
- # FIXME: yard with @yield
- def create_object(object, pretty_name = nil, object_class: nil)
- output = if object_class
- edit_data(object, object_class: object_class)
- else
- edit_hash(object)
- end
-
- if Kernel.block_given?
- output = yield(output)
- else
- output.save
- end
-
- pretty_name ||= output
-
- msg("Created #{pretty_name}")
-
- output(output) if config[:print_after]
- end
-
- # FIXME: yard with @yield
- def delete_object(klass, name, delete_name = nil)
- confirm("Do you really want to delete #{name}")
-
- if Kernel.block_given?
- object = yield
- else
- object = klass.load(name)
- object.destroy
- end
-
- output(format_for_display(object)) if config[:print_after]
-
- obj_name = delete_name ? "#{delete_name}[#{name}]" : object
- msg("Deleted #{obj_name}")
- end
-
- # helper method for testing if a field exists
- # and returning the usage and proper error if not
- def test_mandatory_field(field, fieldname)
- if field.nil?
- show_usage
- ui.fatal("You must specify a #{fieldname}")
- exit 1
- end
- end
-
- def rest
- @rest ||= begin
- require_relative "server_api"
- Chef::ServerAPI.new(Chef::Config[:chef_server_url])
- end
- end
-
- def noauth_rest
- @rest ||= begin
- require_relative "http/simple_json"
- Chef::HTTP::SimpleJSON.new(Chef::Config[:chef_server_url])
- end
- end
-
- def server_url
- Chef::Config[:chef_server_url]
- end
-
- def maybe_setup_fips
- unless config[:fips].nil?
- Chef::Config[:fips] = config[:fips]
- end
- Chef::Config.init_openssl
- end
- end
-end
diff --git a/lib/chef/knife/acl_add.rb b/lib/chef/knife/acl_add.rb
deleted file mode 100644
index 144a18fcb1..0000000000
--- a/lib/chef/knife/acl_add.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# 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
deleted file mode 100644
index 0835d1ac05..0000000000
--- a/lib/chef/knife/acl_base.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-#
-# 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
deleted file mode 100644
index 4992fe2afa..0000000000
--- a/lib/chef/knife/acl_bulk_add.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-#
-# 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
deleted file mode 100644
index 0f35f1e2fb..0000000000
--- a/lib/chef/knife/acl_bulk_remove.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-# 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
deleted file mode 100644
index 13f089ff3e..0000000000
--- a/lib/chef/knife/acl_remove.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# 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
deleted file mode 100644
index d3a5002b30..0000000000
--- a/lib/chef/knife/acl_show.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# 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
deleted file mode 100644
index 1550c62dc1..0000000000
--- a/lib/chef/knife/bootstrap.rb
+++ /dev/null
@@ -1,1142 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-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
- 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."
-
- option :ssh_gateway_identity,
- long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
- description: "The SSH identity file used for gateway authentication."
-
- 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."
-
- 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"
-
- #
- # bootstrap options
- #
-
- # client.rb content via chef-full/bootstrap_context
- option :bootstrap_version,
- 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."
-
- # 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."
-
- # 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."
-
- # 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"
-
- # 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
- }
-
- # 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
-
- # 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
-
- # runtime - sudo settings (train handles sudo)
- option :use_sudo_password,
- 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: []
-
- # 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
-
- # 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
-
- # 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: []
-
- # 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 #{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 #{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."
-
- 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 #{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 #{ChefUtils::Dist::Infra::PRODUCT}."
-
- # bootstrap template
- option :bootstrap_curl_options,
- 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."
-
- # 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."
-
- # 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, accumulator|
- (vault, item) = i.split(":")
- accumulator ||= {}
- accumulator[vault] ||= []
- accumulator[vault].push(item)
- accumulator
- }
-
- # 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,
- config: config,
- ui: ui
- )
- end
-
- def chef_vault_handler
- @chef_vault_handler ||= Chef::Knife::Bootstrap::ChefVaultHandler.new(
- config: config,
- ui: ui
- )
- end
-
- # 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
- if connection.windows?
- "windows-chef-client-msi"
- else
- "chef-full"
- end
- end
-
- def host_descriptor
- Array(@name_args).first
- end
-
- # The server_name is the DNS or IP we are going to connect to, it is not necessarily
- # the node name, the fqdn, or the hostname of the server. This is a public API hook
- # which knife plugins use or inherit and override.
- #
- # @return [String] The DNS or IP that bootstrap will connect to
- def server_name
- if host_descriptor
- @server_name ||= host_descriptor.split("@").reverse[0]
- end
- end
-
- # @return [String] The CLI specific bootstrap template or the default
- def 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.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(__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.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
- File.exist?(bootstrap_template)
- end
-
- unless template_file
- ui.info("Can not find bootstrap definition for #{template}")
- raise Errno::ENOENT
- end
-
- Chef::Log.trace("Found bootstrap template: #{template_file}")
-
- template_file
- end
-
- def secret
- @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 ||=
- 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
- @config[:first_boot_attributes] || @config[:first_boot_attributes_from_file] || {}
- end
-
- def render_template
- @config[:first_boot_attributes] = first_boot_attributes
- template_file = find_template
- template = IO.read(template_file).chomp
- Erubis::Eruby.new(template).evaluate(bootstrap_context)
- end
-
- def run
- check_license if ChefUtils::Dist::Org::ENFORCE_LICENSE
-
- plugin_setup!
- validate_name_args!
- 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])))
-
- 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.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
-
- 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
-
- 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.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
- end
- end
-
- # 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
- elsif policyfile_and_run_list_given?
- 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
-
- # 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 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 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]
- 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
-
- 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?
- end
-
- def run_list_given?
- !config[:run_list].nil? && !config[:run_list].empty?
- end
-
- def policyfile_options_given?
- !!config[:policy_name]
- end
-
- # True if one of policy_name or policy_group was given, but not both
- def incomplete_policyfile_options?
- (!!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
deleted file mode 100644
index 20759d6fdf..0000000000
--- a/lib/chef/knife/bootstrap/chef_vault_handler.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-#
-# Author:: Lamont Granquist (<lamont@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 Knife
- class Bootstrap < Knife
- class ChefVaultHandler
-
- # @return [Hash] knife merged config, typically @config
- attr_accessor :config
-
- # @return [Chef::Knife::UI] ui object for output
- attr_accessor :ui
-
- # @return [Chef::ApiClient] vault client
- attr_reader :client
-
- # @param config [Hash] knife merged config, typically @config
- # @param ui [Chef::Knife::UI] ui object for output
- 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.
- #
- # @param client [Chef::ApiClient] vault client
- def run(client)
- return unless doing_chef_vault?
-
- sanity_check
-
- @client = client
-
- update_bootstrap_vault_json!
- end
-
- # Iterate through all the vault items to update. Items may be either a String
- # or an Array of Strings:
- #
- # {
- # "vault1": "item",
- # "vault2": [ "item1", "item2", "item2" ]
- # }
- #
- def update_bootstrap_vault_json!
- vault_json.each do |vault, items|
- [ items ].flatten.each do |item|
- update_vault(vault, item)
- end
- end
- end
-
- # @return [Boolean] if we've got chef vault options to act on or not
- def doing_chef_vault?
- !!(bootstrap_vault_json || bootstrap_vault_file || bootstrap_vault_item)
- end
-
- private
-
- # warn if the user has given mutual conflicting options
- def sanity_check
- if bootstrap_vault_item && (bootstrap_vault_json || bootstrap_vault_file)
- ui.warn "--vault-item given with --vault-list or --vault-file, ignoring the latter"
- end
-
- if bootstrap_vault_json && bootstrap_vault_file
- ui.warn "--vault-list given with --vault-file, ignoring the latter"
- end
- end
-
- # @return [String] string with serialized JSON representing the chef vault items
- def bootstrap_vault_json
- config[:bootstrap_vault_json]
- end
-
- # @return [String] JSON text in a file representing the chef vault items
- def bootstrap_vault_file
- config[:bootstrap_vault_file]
- end
-
- # @return [Hash] Ruby object representing the chef vault items to create
- def bootstrap_vault_item
- config[:bootstrap_vault_item]
- end
-
- # 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
- def vault_json
- @vault_json ||=
- begin
- if bootstrap_vault_item
- bootstrap_vault_item
- else
- json = bootstrap_vault_json || File.read(bootstrap_vault_file)
- Chef::JSONCompat.from_json(json)
- end
- end
- end
-
- # Update an individual vault item and save it
- #
- # @param vault [String] name of the chef-vault encrypted data bag
- # @param item [String] name of the chef-vault encrypted item
- def update_vault(vault, item)
- require_chef_vault!
- bootstrap_vault_item = load_chef_bootstrap_vault_item(vault, item)
- bootstrap_vault_item.clients(client)
- bootstrap_vault_item.save
- end
-
- # Hook to stub out ChefVault
- #
- # @param vault [String] name of the chef-vault encrypted data bag
- # @param item [String] name of the chef-vault encrypted item
- # @return [ChefVault::Item] ChefVault::Item object
- def load_chef_bootstrap_vault_item(vault, item)
- ChefVault::Item.load(vault, item)
- end
-
- public :load_chef_bootstrap_vault_item # for stubbing
-
- # Helper to very lazily require the chef-vault gem
- 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 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
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/bootstrap/client_builder.rb b/lib/chef/knife/bootstrap/client_builder.rb
deleted file mode 100644
index d9c3d83d06..0000000000
--- a/lib/chef/knife/bootstrap/client_builder.rb
+++ /dev/null
@@ -1,212 +0,0 @@
-#
-# Author:: Lamont Granquist (<lamont@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 "../../node"
-require_relative "../../server_api"
-require_relative "../../api_client/registration"
-require_relative "../../api_client"
-require "tmpdir" unless defined?(Dir.mktmpdir)
-
-class Chef
- class Knife
- class Bootstrap < Knife
- class ClientBuilder
-
- # @return [Hash] knife merged config, typically @config
- attr_accessor :config
- # @return [Hash] chef config object
- attr_accessor :chef_config
- # @return [Chef::Knife::UI] ui object for output
- attr_accessor :ui
- # @return [Chef::ApiClient] client saved on run
- attr_reader :client
-
- # @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(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
- # the new client, then create the new node.
- def run
- sanity_check
-
- ui.info("Creating new client for #{node_name}")
-
- @client = create_client!
-
- ui.info("Creating new node for #{node_name}")
-
- create_node!
- end
-
- # Tempfile to use to write newly created client credentials to.
- #
- # This method is public so that the knife bootstrapper can read then and pass the value into
- # the handler for chef vault which needs the client cert we create here.
- #
- # We hang onto the tmpdir as an ivar as well so that it will not get GC'd and removed
- #
- # @return [String] path to the generated client.pem
- def client_path
- @client_path ||=
- begin
- @tmpdir = Dir.mktmpdir
- File.join(@tmpdir, "#{node_name}.pem")
- end
- end
-
- private
-
- # @return [String] node name from the config
- def node_name
- config[:chef_node_name]
- end
-
- # @return [String] environment from the config
- def environment
- config[:environment]
- end
-
- # @return [String] run_list from the config
- def run_list
- config[:run_list]
- end
-
- # @return [String] policy_name from the config
- def policy_name
- config[:policy_name]
- end
-
- # @return [String] policy_group from the config
- def policy_group
- config[:policy_group]
- end
-
- # @return [Hash,Array] Object representation of json first-boot attributes from the config
- def first_boot_attributes
- config[:first_boot_attributes]
- end
-
- # @return [String] chef server url from the Chef::Config
- def chef_server_url
- chef_config[:chef_server_url]
- end
-
- # Accesses the run_list and coerces it into an Array, changing nils into
- # the empty Array, and splitting strings representations of run_lists into
- # Arrays.
- #
- # @return [Array] run_list coerced into an array
- def normalized_run_list
- case run_list
- when nil
- []
- when String
- run_list.split(/\s*,\s*/)
- when Array
- run_list
- end
- end
-
- # Create the client object and save it to the Chef API
- def create_client!
- Chef::ApiClient::Registration.new(node_name, client_path, http_api: rest).run
- end
-
- # Create the node object (via the lazy accessor) and save it to the Chef API
- def create_node!
- node.save
- end
-
- # Create a new Chef::Node. Supports creating the node with its name, run_list, attributes
- # and environment. This injects a rest object into the Chef::Node which uses the client key
- # for authentication so that the client creates the node and therefore we get the acls setup
- # correctly.
- #
- # @return [Chef::Node] new chef node to create
- def node
- @node ||=
- begin
- node = Chef::Node.new(chef_server_rest: client_rest)
- node.name(node_name)
- node.run_list(normalized_run_list)
- node.normal_attrs = first_boot_attributes if first_boot_attributes
- node.environment(environment) if environment
- node.policy_name = policy_name if policy_name
- node.policy_group = policy_group if policy_group
- (config[:tags] || []).each do |tag|
- node.tags << tag
- end
- node
- end
- end
-
- # Check for the existence of a node and/or client already on the server. If the node
- # already exists, we must delete it in order to proceed so that we can create a new node
- # object with the permissions of the new client. There is a use case for creating a new
- # client and wiring it up to a precreated node object, but we do currently support that.
- #
- # We prompt the user about what to do and will fail hard if we do not get confirmation to
- # delete any prior node/client objects.
- def sanity_check
- if resource_exists?("nodes/#{node_name}")
- ui.confirm("Node #{node_name} exists, overwrite it")
- rest.delete("nodes/#{node_name}")
- end
- if resource_exists?("clients/#{node_name}")
- ui.confirm("Client #{node_name} exists, overwrite it")
- rest.delete("clients/#{node_name}")
- end
- end
-
- # Check if an relative path exists on the chef server
- #
- # @param relative_path [String] URI path relative to the chef organization
- # @return [Boolean] if the relative path exists or returns a 404
- def resource_exists?(relative_path)
- rest.get(relative_path)
- true
- 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)
- end
-
- # @return [Chef::ServerAPI] REST client using the cli user's knife credentials
- # this uses the users's credentials
- def rest
- @rest ||= Chef::ServerAPI.new(chef_server_url)
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/bootstrap/templates/README.md b/lib/chef/knife/bootstrap/templates/README.md
deleted file mode 100644
index 7f28f8f40f..0000000000
--- a/lib/chef/knife/bootstrap/templates/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-This directory contains bootstrap templates which can be used with the -d flag
-to 'knife bootstrap' to install Chef in different ways. To simplify installation,
-and reduce the matrix of common installation patterns to support, we have
-standardized on the [Omnibus](https://github.com/chef/omnibus) built installation
-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/) 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/#custom-templates).
diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb
deleted file mode 100644
index 2e0c80eaef..0000000000
--- a/lib/chef/knife/bootstrap/templates/chef-full.erb
+++ /dev/null
@@ -1,242 +0,0 @@
-<%= "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"
-else
- tmp=$TMPDIR
-fi
-
-# secure-ish temp dir creation without having mktemp available (DDoS-able but not exploitable)
-tmp_dir="$tmp/install.sh.$$"
-(umask 077 && mkdir $tmp_dir) || exit 1
-
-exists() {
- if command -v $1 >/dev/null 2>&1
- then
- return 0
- else
- return 1
- fi
-}
-
-http_404_error() {
- echo "ERROR 404: Could not retrieve a valid install.sh!"
- exit 1
-}
-
-capture_tmp_stderr() {
- # spool up /tmp/stderr from all the commands we called
- if test -f "$tmp_dir/stderr"; then
- output=`cat $tmp_dir/stderr`
- stderr_results="${stderr_results}\nSTDERR from $1:\n\n$output\n"
- rm $tmp_dir/stderr
- fi
-}
-
-# do_wget URL FILENAME
-do_wget() {
- echo "trying wget..."
- 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
- if test $? -eq 0; then
- http_404_error
- fi
-
- # check for bad return status or empty output
- if test $rc -ne 0 || test ! -s "$2"; then
- capture_tmp_stderr "wget"
- return 1
- fi
-
- return 0
-}
-
-# do_curl URL FILENAME
-do_curl() {
- echo "trying curl..."
- 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
- if test $? -eq 0; then
- http_404_error
- fi
-
- # check for bad return status or empty output
- if test $rc -ne 0 || test ! -s "$2"; then
- capture_tmp_stderr "curl"
- return 1
- fi
-
- return 0
-}
-
-# do_fetch URL FILENAME
-do_fetch() {
- echo "trying fetch..."
- fetch -o "$2" "$1" 2>$tmp_dir/stderr
- # check for bad return status
- test $? -ne 0 && return 1
- return 0
-}
-
-# do_perl URL FILENAME
-do_perl() {
- echo "trying perl..."
- perl -e "use LWP::Simple; getprint(shift @ARGV);" "$1" > "$2" 2>$tmp_dir/stderr
- rc=$?
- # check for 404
- grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null
- if test $? -eq 0; then
- http_404_error
- fi
-
- # check for bad return status or empty output
- if test $rc -ne 0 || test ! -s "$2"; then
- capture_tmp_stderr "perl"
- return 1
- fi
-
- return 0
-}
-
-# do_python URL FILENAME
-do_python() {
- echo "trying python..."
- python -c "import sys,urllib2 ; sys.stdout.write(urllib2.urlopen(sys.argv[1]).read())" "$1" > "$2" 2>$tmp_dir/stderr
- rc=$?
- # check for 404
- grep "HTTP Error 404" $tmp_dir/stderr 2>&1 >/dev/null
- if test $? -eq 0; then
- http_404_error
- fi
-
- # check for bad return status or empty output
- if test $rc -ne 0 || test ! -s "$2"; then
- capture_tmp_stderr "python"
- return 1
- fi
- return 0
-}
-
-# do_download URL FILENAME
-do_download() {
- PATH=/opt/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sfw/bin:/sbin:/bin:/usr/sbin:/usr/bin
- export PATH
-
- echo "downloading $1"
- echo " to file $2"
-
- # we try all of these until we get success.
- # perl, in particular may be present but LWP::Simple may not be installed
-
- if exists wget; then
- do_wget $1 $2 && return 0
- fi
-
- if exists curl; then
- do_curl $1 $2 && return 0
- fi
-
- if exists fetch; then
- do_fetch $1 $2 && return 0
- fi
-
- if exists perl; then
- do_perl $1 $2 && return 0
- fi
-
- if exists python; then
- do_python $1 $2 && return 0
- fi
-
- echo ">>>>>> wget, curl, fetch, perl, or python not found on this instance."
-
- if test "x$stderr_results" != "x"; then
- echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results"
- fi
-
- return 16
-}
-
-<%# 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="<%= @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 (<%= @config[:channel] %>/<%= version_to_install %>)"
- do_download ${install_sh} $tmp_dir/install.sh
- sh $tmp_dir/install.sh -P <%= @config[:bootstrap_product] %> -c <%= @config[:channel] %> -v <%= version_to_install %>
- fi
-<% end %>
-
-if test "x$tmp_dir" != "x"; then
- rm -r "$tmp_dir"
-fi
-
-mkdir -p /etc/chef
-
-<% if client_pem -%>
-(umask 077 && (cat > /etc/chef/client.pem <<'EOP'
-<%= ::File.read(::File.expand_path(client_pem)) %>
-EOP
-)) || exit 1
-<% end -%>
-
-<% if validation_key -%>
-(umask 077 && (cat > /etc/chef/validation.pem <<'EOP'
-<%= validation_key %>
-EOP
-)) || exit 1
-<% end -%>
-
-<% if encrypted_data_bag_secret -%>
-(umask 077 && (cat > /etc/chef/encrypted_data_bag_secret <<'EOP'
-<%= encrypted_data_bag_secret %>
-EOP
-)) || exit 1
-<% end -%>
-
-<% unless trusted_certs.empty? -%>
-mkdir -p /etc/chef/trusted_certs
-<%= trusted_certs %>
-<% end -%>
-
-<%# Generate Ohai Hints -%>
-<% unless @config[:hints].nil? || @config[:hints].empty? -%>
-mkdir -p /etc/chef/ohai/hints
-
-<% @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'
-<%= config_content %>
-EOP
-
-cat > /etc/chef/first-boot.json <<'EOP'
-<%= Chef::JSONCompat.to_json(first_boot) %>
-EOP
-
-<% unless client_d.empty? -%>
-mkdir -p /etc/chef/client.d
-<%= client_d %>
-<% end -%>
-
-echo "Starting the first <%= ChefUtils::Dist::Infra::PRODUCT %> Client run..."
-
-<%= 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
deleted file mode 100644
index 7aa7be49f8..0000000000
--- a/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
+++ /dev/null
@@ -1,278 +0,0 @@
-@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
deleted file mode 100644
index a220ece5bc..0000000000
--- a/lib/chef/knife/bootstrap/train_connector.rb
+++ /dev/null
@@ -1,336 +0,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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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
deleted file mode 100644
index 38d25583b3..0000000000
--- a/lib/chef/knife/client_bulk_delete.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientBulkDelete < Knife
-
- deps do
- require_relative "../api_client_v1"
- end
-
- option :delete_validators,
- short: "-D",
- long: "--delete-validators",
- description: "Force deletion of clients if they're validators."
-
- banner "knife client bulk delete REGEX (options)"
-
- def run
- if name_args.length < 1
- ui.fatal("You must supply a regular expression to match the results against")
- exit 42
- end
- all_clients = Chef::ApiClientV1.list(true)
-
- matcher = /#{name_args[0]}/
- clients_to_delete = {}
- validators_to_delete = {}
- all_clients.each do |name, client|
- next unless name&.match?(matcher)
-
- if client.validator
- validators_to_delete[client.name] = client
- else
- clients_to_delete[client.name] = client
- end
- end
-
- if clients_to_delete.empty? && validators_to_delete.empty?
- ui.info "No clients match the expression /#{name_args[0]}/"
- exit 0
- end
-
- check_and_delete_validators(validators_to_delete)
- check_and_delete_clients(clients_to_delete)
- end
-
- def check_and_delete_validators(validators)
- unless validators.empty?
- unless config[:delete_validators]
- ui.msg("The following clients are validators and will not be deleted:")
- print_clients(validators)
- ui.msg("You must specify --delete-validators to delete the validator clients")
- else
- ui.msg("The following validators will be deleted:")
- print_clients(validators)
- if ui.confirm_without_exit("Are you sure you want to delete these validators")
- destroy_clients(validators)
- end
- end
- end
- end
-
- def check_and_delete_clients(clients)
- unless clients.empty?
- ui.msg("The following clients will be deleted:")
- print_clients(clients)
- ui.confirm("Are you sure you want to delete these clients")
- destroy_clients(clients)
- end
- end
-
- def destroy_clients(clients)
- clients.sort.each do |name, client|
- client.destroy
- ui.msg("Deleted client #{name}")
- end
- end
-
- def print_clients(clients)
- ui.msg("")
- ui.msg(ui.list(clients.keys.sort, :columns_down))
- ui.msg("")
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_create.rb b/lib/chef/knife/client_create.rb
deleted file mode 100644
index d6e0eab63b..0000000000
--- a/lib/chef/knife/client_create.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class ClientCreate < Knife
-
- deps do
- require_relative "../api_client_v1"
- end
-
- option :file,
- 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
-
- 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)."
-
- option :prevent_keygen,
- 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)"
-
- def client
- @client_field ||= Chef::ApiClientV1.new
- end
-
- def create_client(client)
- # should not be using save :( bad behavior
- Chef::ApiClientV1.from_hash(client).save
- end
-
- def run
- test_mandatory_field(@name_args[0], "client name")
- client.name @name_args[0]
-
- if config[:public_key] && config[:prevent_keygen]
- show_usage
- ui.fatal("You cannot pass --public-key and --prevent-keygen")
- exit 1
- end
-
- if !config[:prevent_keygen] && !config[:public_key]
- client.create_key(true)
- end
-
- if config[:validator]
- client.validator(true)
- end
-
- if config[:public_key]
- client.public_key File.read(File.expand_path(config[:public_key]))
- end
-
- output = edit_hash(client)
- final_client = create_client(output)
- ui.info("Created #{final_client}")
-
- # output private_key if one
- if final_client.private_key
- if config[:file]
- File.open(config[:file], "w") do |f|
- f.print(final_client.private_key)
- end
- else
- puts final_client.private_key
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
deleted file mode 100644
index 3ecfa38242..0000000000
--- a/lib/chef/knife/client_delete.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientDelete < Knife
-
- deps do
- require_relative "../api_client_v1"
- end
-
- option :delete_validators,
- short: "-D",
- long: "--delete-validators",
- description: "Force deletion of client if it's a validator."
-
- banner "knife client delete [CLIENT [CLIENT]] (options)"
-
- def run
- if @name_args.length == 0
- show_usage
- ui.fatal("You must specify at least one client name")
- exit 1
- end
-
- @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}")
- 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
deleted file mode 100644
index f89f5e38ec..0000000000
--- a/lib/chef/knife/client_edit.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientEdit < Knife
-
- deps do
- require_relative "../api_client_v1"
- end
-
- banner "knife client edit CLIENT (options)"
-
- def run
- @client_name = @name_args[0]
-
- if @client_name.nil?
- show_usage
- ui.fatal("You must specify a client name")
- exit 1
- end
-
- 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)
- client.save
- ui.msg("Saved #{client}.")
- else
- ui.msg("Client unchanged, not saving.")
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_key_create.rb b/lib/chef/knife/client_key_create.rb
deleted file mode 100644
index 192d724473..0000000000
--- a/lib/chef/knife/client_key_create.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_create_base"
-
-class Chef
- class Knife
- # Implements knife user key create using Chef::Knife::KeyCreate
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- 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 = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "client"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyCreate.new(@actor, actor_field_name, ui, config)
- end
-
- def actor_missing_error
- "You must specify a client name"
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_key_delete.rb b/lib/chef/knife/client_key_delete.rb
deleted file mode 100644
index 2d486ffcbd..0000000000
--- a/lib/chef/knife/client_key_delete.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- # Implements knife client key delete using Chef::Knife::KeyDelete
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class ClientKeyDelete < Knife
- banner "knife client key delete CLIENT KEYNAME (options)"
-
- deps do
- require_relative "key_delete"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "client"
- end
-
- def actor_missing_error
- "You must specify a client name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyDelete.new(@name, @actor, actor_field_name, ui)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_key_edit.rb b/lib/chef/knife/client_key_edit.rb
deleted file mode 100644
index d178aafc17..0000000000
--- a/lib/chef/knife/client_key_edit.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_edit_base"
-
-class Chef
- class Knife
- # Implements knife client key edit using Chef::Knife::KeyEdit
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class ClientKeyEdit < Knife
- include Chef::Knife::KeyEditBase
-
- banner "knife client key edit CLIENT KEYNAME (options)"
-
- deps do
- require_relative "key_edit"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "client"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyEdit.new(@name, @actor, actor_field_name, ui, config)
- end
-
- def actor_missing_error
- "You must specify a client name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_key_list.rb b/lib/chef/knife/client_key_list.rb
deleted file mode 100644
index afc04335d9..0000000000
--- a/lib/chef/knife/client_key_list.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_list_base"
-
-class Chef
- class Knife
- # Implements knife user key list using Chef::Knife::KeyList
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class ClientKeyList < Knife
- include Chef::Knife::KeyListBase
-
- banner "knife client key list CLIENT (options)"
-
- deps do
- require_relative "key_list"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def list_method
- :list_by_client
- end
-
- def actor_missing_error
- "You must specify a client name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyList.new(@actor, list_method, ui, config)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_key_show.rb b/lib/chef/knife/client_key_show.rb
deleted file mode 100644
index 14e1f0ca7a..0000000000
--- a/lib/chef/knife/client_key_show.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- # Implements knife client key show using Chef::Knife::KeyShow
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class ClientKeyShow < Knife
- banner "knife client key show CLIENT KEYNAME (options)"
-
- deps do
- require_relative "key_show"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def load_method
- :load_by_client
- end
-
- def actor_missing_error
- "You must specify a client name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyShow.new(@name, @actor, load_method, ui)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_list.rb b/lib/chef/knife/client_list.rb
deleted file mode 100644
index b4fc46767b..0000000000
--- a/lib/chef/knife/client_list.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientList < Knife
-
- deps do
- require_relative "../api_client_v1"
- end
-
- banner "knife client list (options)"
-
- option :with_uri,
- short: "-w",
- long: "--with-uri",
- description: "Show corresponding URIs."
-
- def run
- output(format_list_for_display(Chef::ApiClientV1.list))
- end
- end
- end
-end
diff --git a/lib/chef/knife/client_reregister.rb b/lib/chef/knife/client_reregister.rb
deleted file mode 100644
index 6741895b23..0000000000
--- a/lib/chef/knife/client_reregister.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientReregister < Knife
-
- deps do
- 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."
-
- def run
- @client_name = @name_args[0]
-
- if @client_name.nil?
- show_usage
- ui.fatal("You must specify a client name")
- exit 1
- end
-
- client = Chef::ApiClientV1.reregister(@client_name)
- Chef::Log.trace("Updated client data: #{client.inspect}")
- key = client.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/client_show.rb b/lib/chef/knife/client_show.rb
deleted file mode 100644
index 9170c73085..0000000000
--- a/lib/chef/knife/client_show.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ClientShow < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require_relative "../api_client_v1"
- end
-
- banner "knife client show CLIENT (options)"
-
- def run
- @client_name = @name_args[0]
-
- if @client_name.nil?
- show_usage
- ui.fatal("You must specify a client name")
- exit 1
- end
-
- client = Chef::ApiClientV1.load(@client_name)
- output(format_for_display(client))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/config_get.rb b/lib/chef/knife/config_get.rb
deleted file mode 100644
index 91e6b7affd..0000000000
--- a/lib/chef/knife/config_get.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# 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
deleted file mode 100644
index a355c531fe..0000000000
--- a/lib/chef/knife/config_get_profile.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# 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
deleted file mode 100644
index c9f821e2a8..0000000000
--- a/lib/chef/knife/config_list.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-#
-# 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
deleted file mode 100644
index c037b0de53..0000000000
--- a/lib/chef/knife/config_list_profiles.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# 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
deleted file mode 100644
index 7f28891885..0000000000
--- a/lib/chef/knife/config_show.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-#
-# 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
deleted file mode 100644
index e944dc210b..0000000000
--- a/lib/chef/knife/config_use.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# 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
deleted file mode 100644
index 169bdbef30..0000000000
--- a/lib/chef/knife/config_use_profile.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# 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
deleted file mode 100644
index 2a27fd5d88..0000000000
--- a/lib/chef/knife/configure.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class Configure < Knife
- attr_reader :chef_server, :new_client_name, :admin_client_name, :admin_client_key
- attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key
-
- deps do
- 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
-
- banner "knife configure (options)"
-
- option :repository,
- 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."
-
- option :admin_client_name,
- 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."
-
- option :validation_client_name,
- 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."
-
- def configure_chef
- # We are just faking out the system so that you can do this without a key specified
- Chef::Config[:node_name] = "woot"
- super
- Chef::Config[:node_name] = nil
- end
-
- def run
- FileUtils.mkdir_p(chef_config_path)
-
- ask_user_for_config
-
- 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]
- ui.msg("Creating initial API user...")
- Chef::Config[:chef_server_url] = chef_server
- Chef::Config[:node_name] = admin_client_name
- Chef::Config[:client_key] = admin_client_key
- 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: ", echo: false)
- user_create.config[:admin] = true
- user_create.config[:file] = new_client_key
- user_create.config[:yes] = true
- user_create.config[:disable_editing] = true
- user_create.run
- else
- ui.msg("*****")
- ui.msg("")
- ui.msg("You must place your client key in:")
- ui.msg(" #{new_client_key}")
- ui.msg("Before running commands with Knife")
- ui.msg("")
- ui.msg("*****")
- 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}/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: "#{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)
- end
-
- @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.all_plugins(%w{ os hostname fqdn })
- o[:fqdn] || o[:machinename] || o[:hostname] || "localhost"
- end
-
- # @return [String] the path to the user's .chef directory
- def chef_config_path
- @chef_config_path ||= Chef::Util::PathHelper.home(".chef")
- end
-
- # @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
-end
diff --git a/lib/chef/knife/configure_client.rb b/lib/chef/knife/configure_client.rb
deleted file mode 100644
index c6f159ec8f..0000000000
--- a/lib/chef/knife/configure_client.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 ConfigureClient < Knife
- banner "knife configure client DIRECTORY"
-
- def run
- unless @config_dir = @name_args[0]
- ui.fatal "You must provide the directory to put the files in"
- show_usage
- exit(1)
- end
-
- ui.info("Creating client configuration")
- FileUtils.mkdir_p(@config_dir)
- ui.info("Writing client.rb")
- File.open(File.join(@config_dir, "client.rb"), "w") do |file|
- file.puts("chef_server_url '#{Chef::Config[:chef_server_url]}'")
- file.puts("validation_client_name '#{Chef::Config[:validation_client_name]}'")
- end
- ui.info("Writing validation.pem")
- File.open(File.join(@config_dir, "validation.pem"), "w") do |validation|
- validation.puts(IO.read(Chef::Config[:validation_key]))
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_bulk_delete.rb b/lib/chef/knife/cookbook_bulk_delete.rb
deleted file mode 100644
index d6657ccb4f..0000000000
--- a/lib/chef/knife/cookbook_bulk_delete.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# 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 "../knife"
-
-class Chef
- class Knife
- class CookbookBulkDelete < Knife
-
- deps do
- 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."
-
- banner "knife cookbook bulk delete REGEX (options)"
-
- def run
- unless regex_str = @name_args.first
- ui.fatal("You must supply a regular expression to match the results against")
- exit 42
- end
-
- regex = Regexp.new(regex_str)
-
- all_cookbooks = Chef::CookbookVersion.list
- cookbooks_names = all_cookbooks.keys.grep(regex)
- cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name]; hash }
- ui.msg "All versions of the following cookbooks will be deleted:"
- ui.msg ""
- ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down)
- ui.msg ""
-
- unless config[:yes]
- ui.confirm("Do you really want to delete these cookbooks")
-
- if config[:purge]
- ui.msg("Files that are common to multiple cookbooks are shared, so purging the files may break other cookbooks.")
- ui.confirm("Are you sure you want to purge files instead of just deleting the cookbooks")
- end
- ui.msg ""
- end
-
- cookbooks_names.each do |cookbook_name|
- versions = rest.get("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map { |v| v["version"] }.flatten
- versions.each do |version|
- rest.delete("cookbooks/#{cookbook_name}/#{version}#{config[:purge] ? "?purge=true" : ""}")
- ui.info("Deleted cookbook #{cookbook_name.ljust(25)} [#{version}]")
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_delete.rb b/lib/chef/knife/cookbook_delete.rb
deleted file mode 100644
index 04ecb95cf4..0000000000
--- a/lib/chef/knife/cookbook_delete.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 CookbookDelete < Knife
-
- attr_accessor :cookbook_name, :version
-
- deps do
- require_relative "../cookbook_version"
- end
-
- 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."
-
- banner "knife cookbook delete COOKBOOK VERSION (options)"
-
- def run
- confirm("Files that are common to multiple cookbooks are shared, so purging the files may disable other cookbooks. Are you sure you want to purge files instead of just deleting the cookbook") if config[:purge]
- @cookbook_name, @version = name_args
- if @cookbook_name && @version
- delete_explicit_version
- elsif @cookbook_name && config[:all]
- delete_all_versions
- elsif @cookbook_name && @version.nil?
- delete_without_explicit_version
- elsif @cookbook_name.nil?
- show_usage
- ui.fatal("You must provide the name of the cookbook to delete.")
- exit(1)
- end
- end
-
- def delete_explicit_version
- delete_object(Chef::CookbookVersion, "#{@cookbook_name} version #{@version}", "cookbook") do
- delete_request("cookbooks/#{@cookbook_name}/#{@version}")
- end
- end
-
- def delete_all_versions
- confirm("Do you really want to delete all versions of #{@cookbook_name}")
- delete_all_without_confirmation
- end
-
- def delete_all_without_confirmation
- # look up the available versions again just in case the user
- # got to the list of versions to delete and selected 'all'
- # and also a specific version
- @available_versions = nil
- Array(available_versions).each do |version|
- delete_version_without_confirmation(version)
- end
- end
-
- def delete_without_explicit_version
- if available_versions.nil?
- # we already logged an error or 2 about it, so just bail
- exit(1)
- elsif available_versions.size == 1
- @version = available_versions.first
- delete_explicit_version
- else
- versions_to_delete = ask_which_versions_to_delete
- delete_versions_without_confirmation(versions_to_delete)
- end
- end
-
- def available_versions
- @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::HTTPClientException => e
- if /^404/.match?(e.to_s)
- ui.error("Cannot find a cookbook named #{@cookbook_name} to delete.")
- nil
- else
- raise
- end
- end
-
- def ask_which_versions_to_delete
- question = "Which version(s) do you want to delete?\n"
- valid_responses = {}
- available_versions.each_with_index do |version, index|
- valid_responses[(index + 1).to_s] = version
- question << "#{index + 1}. #{@cookbook_name} #{version}\n"
- 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(&:strip)
-
- if responses.empty?
- ui.error("No versions specified, exiting")
- exit(1)
- end
- versions = responses.map do |response|
- if version = valid_responses[response]
- version
- else
- ui.error("#{response} is not a valid choice, skipping it")
- end
- end
- versions.compact
- end
-
- def delete_version_without_confirmation(version)
- object = delete_request("cookbooks/#{@cookbook_name}/#{version}")
- output(format_for_display(object)) if config[:print_after]
- ui.info("Deleted cookbook[#{@cookbook_name}][#{version}]")
- end
-
- def delete_versions_without_confirmation(versions)
- versions.each do |version|
- if version == :all
- delete_all_without_confirmation
- break
- else
- delete_version_without_confirmation(version)
- end
- end
- end
-
- private
-
- def delete_request(path)
- path += "?purge=true" if config[:purge]
- rest.delete(path)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_download.rb b/lib/chef/knife/cookbook_download.rb
deleted file mode 100644
index a07b519511..0000000000
--- a/lib/chef/knife/cookbook_download.rb
+++ /dev/null
@@ -1,142 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Walters (<cw@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 CookbookDownload < Knife
-
- attr_reader :version
- attr_accessor :cookbook_name
-
- deps do
- 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
-
- option :download_directory,
- 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."
-
- # TODO: tim/cw: 5-23-2010: need to implement knife-side
- # specificity for downloads - need to implement --platform and
- # --fqdn here
- def run
- @cookbook_name, @version = @name_args
-
- if @cookbook_name.nil?
- show_usage
- ui.fatal("You must specify a cookbook name")
- exit 1
- elsif @version.nil?
- @version = determine_version
- if @version.nil?
- ui.fatal("No such cookbook found")
- exit 1
- end
- end
-
- ui.info("Downloading #{@cookbook_name} cookbook version #{@version}")
-
- cookbook = Chef::CookbookVersion.load(@cookbook_name, @version)
- manifest = cookbook.cookbook_manifest
-
- basedir = File.join(config[:download_directory], "#{@cookbook_name}-#{cookbook.version}")
- if File.exist?(basedir)
- if config[:force]
- Chef::Log.trace("Deleting #{basedir}")
- FileUtils.rm_rf(basedir)
- else
- ui.fatal("Directory #{basedir} exists, use --force to overwrite")
- exit
- end
- end
-
- manifest.by_parent_directory.each do |segment, files|
- ui.info("Downloading #{segment}")
- files.each do |segment_file|
- dest = File.join(basedir, segment_file["path"].gsub("/", File::SEPARATOR))
- 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)
- end
- end
- ui.info("Cookbook downloaded to #{basedir}")
- end
-
- def determine_version
- if available_versions.nil?
- nil
- elsif available_versions.size == 1
- @version = available_versions.first
- elsif config[:latest]
- @version = available_versions.last
- else
- ask_which_version
- end
- end
-
- def available_versions
- @available_versions ||= begin
- versions = Chef::CookbookVersion.available_versions(@cookbook_name)
- unless versions.nil?
- versions.map! { |version| Chef::Version.new(version) }
- versions.sort!
- end
- versions
- end
- @available_versions
- end
-
- def ask_which_version
- question = "Which version do you want to download?\n"
- valid_responses = {}
- available_versions.each_with_index do |version, index|
- valid_responses[(index + 1).to_s] = version
- question << "#{index + 1}. #{@cookbook_name} #{version}\n"
- end
- question += "\n"
- response = ask_question(question).strip
-
- unless @version = valid_responses[response]
- ui.error("'#{response}' is not a valid value.")
- exit(1)
- end
- @version
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_list.rb b/lib/chef/knife/cookbook_list.rb
deleted file mode 100644
index 719c10f893..0000000000
--- a/lib/chef/knife/cookbook_list.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Nuo Yan (<nuo@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 CookbookList < Knife
-
- banner "knife cookbook list (options)"
-
- option :with_uri,
- short: "-w",
- long: "--with-uri",
- description: "Show corresponding URIs."
-
- option :all_versions,
- short: "-a",
- long: "--all",
- description: "Show all available versions."
-
- def run
- env = config[:environment]
- num_versions = config[:all_versions] ? "num_versions=all" : "num_versions=1"
- api_endpoint = env ? "/environments/#{env}/cookbooks?#{num_versions}" : "/cookbooks?#{num_versions}"
- cookbook_versions = rest.get(api_endpoint)
- ui.output(format_cookbook_list_for_display(cookbook_versions))
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_metadata.rb b/lib/chef/knife/cookbook_metadata.rb
deleted file mode 100644
index 8d8970b1c1..0000000000
--- a/lib/chef/knife/cookbook_metadata.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 CookbookMetadata < Knife
-
- deps do
- 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(":") }
-
- option :all,
- 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]
-
- if config[:all]
- cl = Chef::CookbookLoader.new(config[:cookbook_path])
- cl.load_cookbooks
- cl.each_key do |cname|
- generate_metadata(cname.to_s)
- end
- else
- cookbook_name = @name_args[0]
- if cookbook_name.nil? || cookbook_name.empty?
- ui.error "You must specify the cookbook to generate metadata for, or use the --all option."
- exit 1
- end
- generate_metadata(cookbook_name)
- end
- end
-
- def generate_metadata(cookbook)
- Array(config[:cookbook_path]).reverse_each do |path|
- file = File.expand_path(File.join(path, cookbook, "metadata.rb"))
- if File.exist?(file)
- generate_metadata_from_file(cookbook, file)
- else
- validate_metadata_json(path, cookbook)
- end
- end
- end
-
- def generate_metadata_from_file(cookbook, file)
- ui.info("Generating metadata for #{cookbook} from #{file}")
- md = Chef::Cookbook::Metadata.new
- md.name(cookbook)
- md.from_file(file)
- json_file = File.join(File.dirname(file), "metadata.json")
- File.open(json_file, "w") do |f|
- f.write(Chef::JSONCompat.to_json_pretty(md))
- end
- 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}:"
- ui.stderr.puts
- ui.stderr.puts e.message
- exit 1
- end
-
- def validate_metadata_json(path, cookbook)
- json_file = File.join(path, cookbook, "metadata.json")
- if File.exist?(json_file)
- Chef::Cookbook::Metadata.validate_json(IO.read(json_file))
- end
- rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e
- ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax."
- ui.stderr.puts "in #{json_file}:"
- ui.stderr.puts
- ui.stderr.puts e.message
- exit 1
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_metadata_from_file.rb b/lib/chef/knife/cookbook_metadata_from_file.rb
deleted file mode 100644
index d768213384..0000000000
--- a/lib/chef/knife/cookbook_metadata_from_file.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright (c) 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_relative "../knife"
-
-class Chef
- class Knife
- class CookbookMetadataFromFile < Knife
-
- deps do
- require_relative "../cookbook/metadata"
- end
-
- 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))
-
- @metadata = Chef::Knife::CookbookMetadata.new
- @metadata.generate_metadata_from_file(cookbook, file)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_show.rb b/lib/chef/knife/cookbook_show.rb
deleted file mode 100644
index 0b97fba139..0000000000
--- a/lib/chef/knife/cookbook_show.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 CookbookShow < Knife
-
- deps do
- 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."
-
- option :platform,
- 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."
-
- option :with_uri,
- short: "-w",
- long: "--with-uri",
- description: "Show corresponding URIs."
-
- def run
- cookbook_name, cookbook_version, segment, filename = @name_args
-
- cookbook = Chef::CookbookVersion.load(cookbook_name, cookbook_version) unless cookbook_version.nil?
-
- case @name_args.length
- when 4 # We are showing a specific file
- node = {}
- 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
- key?(name)
- end
- end
-
- manifest_entry = cookbook.preferred_manifest_record(node, segment, filename)
- temp_file = rest.streaming_request(manifest_entry[:url])
-
- # the temp file is cleaned up elsewhere
- temp_file.open if temp_file.closed?
- pretty_print(temp_file.read)
-
- when 3 # We are showing a specific part of the 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}"
- output(format_cookbook_list_for_display(rest.get(api_endpoint)))
- when 0
- show_usage
- ui.fatal("You must specify a cookbook name")
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_upload.rb b/lib/chef/knife/cookbook_upload.rb
deleted file mode 100644
index 9f6f3c4cb2..0000000000
--- a/lib/chef/knife/cookbook_upload.rb
+++ /dev/null
@@ -1,292 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Walters (<cw@chef.io>)
-# Author:: Nuo Yan (<yan.nuo@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_relative "../knife"
-
-class Chef
- class Knife
- class CookbookUpload < Knife
- deps do
- 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 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
-
- option :all,
- 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."
-
- option :concurrency,
- 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
-
- option :depends,
- short: "-d",
- long: "--include-dependencies",
- description: "Also upload cookbook dependencies."
-
- def run
- # Sanity check before we load anything from the server
- 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!
- version_constraints_to_update = {}
- upload_failures = 0
- upload_ok = 0
-
- # 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(&: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 << 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
- 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|
-
- 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
-
- 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
- else
- upload_set = {}
- @name_args.each do |cookbook_name|
-
- 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
- end
- rescue Exceptions::CookbookNotFoundInRepo => e
- ui.error(e.message)
- Log.debug(e)
-
- end
- upload_set
- end
- end
-
- def cookbook_repo
- @cookbook_loader ||= begin
- Chef::Cookbook::FileVendor.fetch_from_disk(config[:cookbook_path])
- Chef::CookbookLoader.new(config[:cookbook_path])
- end
- end
-
- def update_version_constraints(new_version_constraints)
- new_version_constraints.each do |cookbook_name, version|
- environment.cookbook_versions[cookbook_name] = "= #{version}"
- end
- environment.save
- end
-
- def environment
- @environment ||= config[:environment] ? Environment.load(config[:environment]) : nil
- end
-
- private
-
- def assert_environment_valid!
- environment
- 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)
- exit 1
- else
- raise
- end
- end
-
- def upload(cookbooks, justify_width)
- cookbooks.each do |cb|
- ui.info("Uploading #{cb.name.to_s.ljust(justify_width + 10)} [#{cb.version}]")
- check_for_broken_links!(cb)
- check_for_dependencies!(cb)
- end
- Chef::CookbookUploader.new(cookbooks, force: config[:force], concurrency: config[:concurrency]).upload_cookbooks
- rescue Chef::Exceptions::CookbookFrozen => e
- ui.error e
- raise
- end
-
- def check_for_broken_links!(cookbook)
- # MUST!! dup the cookbook version object--it memoizes its
- # manifest object, but the manifest becomes invalid when you
- # regenerate the metadata
- broken_files = cookbook.dup.manifest_records_by_path.select do |path, info|
- !/[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(" ")}"
- exit 1
- end
- end
-
- def check_for_dependencies!(cookbook)
- # for all dependencies, check if the version is on the server, or
- # the version is in the cookbooks being uploaded. If not, exit and warn the user.
- missing_dependencies = cookbook.metadata.dependencies.reject do |cookbook_name, version|
- check_server_side_cookbooks(cookbook_name, version) || check_uploading_cookbooks(cookbook_name, version)
- end
-
- unless missing_dependencies.empty?
- 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(", ")}"
- exit 1
- end
- end
-
- def check_server_side_cookbooks(cookbook_name, version)
- if @server_side_cookbooks[cookbook_name].nil?
- false
- else
- versions = @server_side_cookbooks[cookbook_name]["versions"].collect { |versions| versions["version"] }
- 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"
- return true
- end
- end
- false
- end
- end
-
- def check_uploading_cookbooks(cookbook_name, version)
- if (! cookbooks_to_upload[cookbook_name].nil?) && Chef::VersionConstraint.new(version).include?(cookbooks_to_upload[cookbook_name].version)
- Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to a local cookbook."
- return true
- end
- false
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
deleted file mode 100644
index 9aa81da82f..0000000000
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ /dev/null
@@ -1,264 +0,0 @@
-#
-# 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 "../../run_list"
-require_relative "../../util/path_helper"
-require "pathname" unless defined?(Pathname)
-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 BootstrapContext
-
- attr_accessor :client_pem
- attr_accessor :config
- attr_accessor :chef_config
-
- def initialize(config, run_list, chef_config, secret = nil)
- @config = config
- @run_list = run_list
- @chef_config = chef_config
- @secret = secret
- end
-
- def bootstrap_environment
- config[:environment]
- end
-
- def 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
- end
-
- def client_d
- @client_d ||= client_d_content
- end
-
- def encrypted_data_bag_secret
- @secret
- end
-
- # Contains commands and content, see trusted_certs_content
- # @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
- chef_server_url "#{chef_config[:chef_server_url]}"
- validation_client_name "#{chef_config[:validation_client_name]}"
- CONFIG
-
- 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? || 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 << %Q{http_proxy "#{config[:bootstrap_proxy]}"\n}
- client_rb << %Q{https_proxy "#{config[:bootstrap_proxy]}"\n}
- end
-
- 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 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 config[:bootstrap_no_proxy]
- client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n}
- end
-
- if encrypted_data_bag_secret
- client_rb << %Q{encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"\n}
- end
-
- unless trusted_certs.empty?
- client_rb << %Q{trusted_certs_dir "/etc/chef/trusted_certs"\n}
- end
-
- 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
- end
-
- 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] || ChefUtils::Dist::Infra::CLIENT
- s = "#{client_path} -j /etc/chef/first-boot.json"
- 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
- end
-
- #
- # 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 config[:channel] == "stable"
- Chef::VERSION.split(".").first
- else
- "latest"
- end
- end
-
- def first_boot
- (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.delete(:run_list) if attributes[:policy_name] && !attributes[:policy_name].empty?
- attributes.merge!(tags: config[:tags]) if config[:tags] && !config[:tags].empty?
- end
- end
-
- private
-
- # 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 << "cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'\n" +
- IO.read(File.expand_path(cert)) + "\nEOP\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 = "/etc/chef/client.d/#{relative}"
- if f.directory?
- content << "mkdir #{file_on_node}\n"
- else
- content << "cat > #{file_on_node} <<'EOP'\n" +
- f.read.gsub("'", "'\\\\''") + "\nEOP\n"
- end
- end
- end
- end
- content
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/cookbook_scm_repo.rb b/lib/chef/knife/core/cookbook_scm_repo.rb
deleted file mode 100644
index ba194a8a6d..0000000000
--- a/lib/chef/knife/core/cookbook_scm_repo.rb
+++ /dev/null
@@ -1,159 +0,0 @@
-#
-# 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 "../../mixin/shell_out"
-
-class Chef
- class Knife
- class CookbookSCMRepo
-
- DIRTY_REPO = /^\s+M/.freeze
-
- include Chef::Mixin::ShellOut
-
- attr_reader :repo_path
- attr_reader :default_branch
- attr_reader :use_current_branch
- attr_reader :ui
-
- def initialize(repo_path, ui, opts = {})
- @repo_path = repo_path
- @ui = ui
- @default_branch = "master"
- @use_current_branch = false
- apply_opts(opts)
- end
-
- def sanity_check
- unless ::File.directory?(repo_path)
- ui.error("The cookbook repo path #{repo_path} does not exist or is not a directory")
- exit 1
- end
- unless git_repo?(repo_path)
- ui.error "The cookbook repo #{repo_path} is not a git repository."
- ui.info("Use `git init` to initialize a git repo")
- exit 1
- end
- if use_current_branch
- @default_branch = get_current_branch
- end
- unless branch_exists?(default_branch)
- ui.error "The default branch '#{default_branch}' does not exist"
- ui.info "If this is a new git repo, make sure you have at least one commit before installing cookbooks"
- exit 1
- end
- cmd = git("status --porcelain")
- 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"
- exit 1
- end
- # TODO: any untracked files in the cookbook directory will get nuked later
- # make this an error condition also.
- true
- end
-
- def reset_to_default_state
- ui.info("Checking out the #{default_branch} branch.")
- git("checkout #{default_branch}")
- end
-
- def prepare_to_import(cookbook_name)
- branch = "chef-vendor-#{cookbook_name}"
- if branch_exists?(branch)
- ui.info("Pristine copy branch (#{branch}) exists, switching to it.")
- git("checkout #{branch}")
- else
- ui.info("Creating pristine copy branch #{branch}")
- git("checkout -b #{branch}")
- end
- end
-
- def finalize_updates_to(cookbook_name, version)
- if update_count = updated?(cookbook_name)
- ui.info "#{update_count} files updated, committing changes"
- git("add #{cookbook_name}")
- git("commit -m \"Import #{cookbook_name} version #{version}\" -- #{cookbook_name}")
- ui.info("Creating tag cookbook-site-imported-#{cookbook_name}-#{version}")
- git("tag -f cookbook-site-imported-#{cookbook_name}-#{version}")
- true
- else
- ui.info("No changes made to #{cookbook_name}")
- false
- end
- end
-
- def merge_updates_from(cookbook_name, version)
- branch = "chef-vendor-#{cookbook_name}"
- Dir.chdir(repo_path) do
- if system("git merge #{branch}")
- ui.info("Cookbook #{cookbook_name} version #{version} successfully installed")
- else
- ui.error("You have merge conflicts - please resolve manually")
- ui.info("Merge status (cd #{repo_path}; git status):")
- system("git status")
- exit 3
- end
- end
- end
-
- def updated?(cookbook_name)
- update_count = git("status --porcelain -- #{cookbook_name}").stdout.strip.lines.count
- update_count == 0 ? nil : update_count
- end
-
- def branch_exists?(branch_name)
- git("branch --no-color").stdout.lines.any? { |l| l =~ /\s#{Regexp.escape(branch_name)}(?:\s|$)/ }
- end
-
- def get_current_branch
- ref = git("symbolic-ref HEAD").stdout
- ref.chomp.split("/")[2]
- end
-
- private
-
- def git_repo?(directory)
- if File.directory?(File.join(directory, ".git"))
- true
- elsif File.dirname(directory) == directory
- false
- else
- git_repo?(File.dirname(directory))
- end
- end
-
- def apply_opts(opts)
- opts.each do |option, value|
- case option.to_s
- when "default_branch"
- @default_branch = value
- when "use_current_branch"
- @use_current_branch = value
- end
- end
- end
-
- def git(command)
- shell_out!("git #{command}", cwd: repo_path)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/core/gem_glob_loader.rb b/lib/chef/knife/core/gem_glob_loader.rb
deleted file mode 100644
index d058379e71..0000000000
--- a/lib/chef/knife/core/gem_glob_loader.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-# Author:: Christopher Brown (<cb@chef.io>)
-# 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 "../../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+}.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
- end
-
- # Returns a Hash of paths to knife commands built-in to chef, or installed via gem.
- # If rubygems is not installed, falls back to globbing the knife directory.
- # The Hash is of the form {"relative/path" => "/absolute/path"}
- #--
- # Note: the "right" way to load the plugins is to require the relative path, i.e.,
- # require 'chef/knife/command'
- # but we're getting frustrated by bugs at every turn, and it's slow besides. So
- # subcommand loader has been modified to load the plugins by using Kernel.load
- # with the absolute path.
- def gem_and_builtin_subcommands
- require "rubygems" unless defined?(Gem)
- find_subcommands_via_rubygems
- rescue LoadError
- find_subcommands_via_dirglob
- end
-
- 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", __dir__)), "*.rb")]
- subcommand_files = {}
- files.each do |knife_file|
- rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/, 1]
- subcommand_files[rel_path] = knife_file
- end
- subcommand_files
- end
-
- def find_subcommands_via_rubygems
- files = find_files_latest_gems "chef/knife/*.rb"
- subcommand_files = {}
- files.each do |file|
- rel_path = file[/(#{Regexp.escape File.join('chef', 'knife', '')}.*)\.rb/, 1]
-
- # When not installed as a gem (ChefDK/appbundler in particular), AND
- # a different version of Chef is installed via gems, `files` will
- # include some files from the 'other' Chef install. If this contains
- # a knife command that doesn't exist in this version of Chef, we will
- # get a LoadError later when we try to require it.
- next if from_different_chef_version?(file)
-
- subcommand_files[rel_path] = file
- end
-
- subcommand_files.merge(find_subcommands_via_dirglob)
- end
-
- private
-
- def find_files_latest_gems(glob, check_load_path = true)
- files = []
-
- if check_load_path
- files = $LOAD_PATH.map do |load_path|
- Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob_dir(load_path)}#{Gem.suffix_pattern}"]
- end.flatten.select { |file| File.file? file.untaint }
- end
-
- gem_files = latest_gem_specs.map do |spec|
- # Gem::Specification#matches_for_glob wasn't added until RubyGems 1.8
- if spec.respond_to? :matches_for_glob
- spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
- else
- check_spec_for_glob(spec, glob)
- end
- end.flatten
-
- files.concat gem_files
- files.uniq! if check_load_path
-
- files
- end
-
- def latest_gem_specs
- @latest_gem_specs ||= if Gem::Specification.respond_to? :latest_specs
- Gem::Specification.latest_specs(true) # find prerelease gems
- else
- Gem.source_index.latest_specs(true)
- end
- end
-
- def check_spec_for_glob(spec, glob)
- dirs = if spec.require_paths.size > 1
- "{#{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(&:untaint)
- end
-
- def from_different_chef_version?(path)
- matches_any_chef_gem?(path) && !matches_this_chef_gem?(path)
- end
-
- def matches_any_chef_gem?(path)
- path =~ MATCHES_CHEF_GEM
- end
-
- def matches_this_chef_gem?(path)
- path =~ MATCHES_THIS_CHEF_GEM
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/generic_presenter.rb b/lib/chef/knife/core/generic_presenter.rb
deleted file mode 100644
index 850bfa8b3d..0000000000
--- a/lib/chef/knife/core/generic_presenter.rb
+++ /dev/null
@@ -1,232 +0,0 @@
-#--
-# 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 "text_formatter"
-
-class Chef
- class Knife
- module Core
-
- # Allows includer knife commands to return multiple attributes
- # @brief knife node show NAME -a ATTR1 -a ATTR2
- module MultiAttributeReturnOption
- # @private
- def self.included(includer)
- includer.class_eval do
- 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] ",
- description: "Show one or more attributes",
- proc: Proc.new { |arg, accumulator|
- accumulator ||= []
- accumulator << arg
- accumulator
- }
- end
- end
- end
-
- # 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.
- class GenericPresenter
-
- attr_reader :ui
- attr_reader :config
-
- # 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)
- @ui, @config = ui, config
- end
-
- # Is the selected output format a data interchange format?
- # Returns true if the selected output format is json or yaml, false
- # otherwise. Knife search uses this to adjust its data output so as not
- # to produce invalid JSON output.
- def interchange?
- case parse_format_option
- when :json, :yaml
- true
- else
- false
- end
- end
-
- # Returns a String representation of +data+ that is suitable for output
- # to a terminal or perhaps for data interchange with another program.
- # The representation of the +data+ depends on the value of the
- # `config[:format]` setting.
- def format(data)
- case parse_format_option
- when :summary
- summarize(data)
- when :text
- text_format(data)
- when :json
- Chef::JSONCompat.to_json_pretty(data)
- when :yaml
- require "yaml" unless defined?(YAML)
- YAML.dump(data)
- when :pp
- 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
- data.values[0]
- else
- out = StringIO.new
- PP.pp(data, out)
- out.string
- end
- end
- end
-
- # Converts the user-supplied value of `config[:format]` to a Symbol
- # representing the desired output format.
- # ===Returns
- # returns one of :summary, :text, :json, :yaml, or :pp
- # ===Raises
- # Raises an ArgumentError if the desired output format could not be
- # determined from the value of `config[:format]`
- def parse_format_option
- case config[:format]
- when "summary", /^s/, nil
- :summary
- when "text", /^t/
- :text
- when "json", /^j/
- :json
- when "yaml", /^y/
- :yaml
- when "pp", /^p/
- :pp
- else
- raise ArgumentError, "Unknown output format #{config[:format]}"
- end
- end
-
- # Summarize the data. Defaults to text format output,
- # which may not be very summary-like
- def summarize(data)
- text_format(data)
- end
-
- # Converts the +data+ to a String in the text format. Uses
- # Chef::Knife::Core::TextFormatter
- def text_format(data)
- TextFormatter.new(data, ui).formatted_data
- end
-
- def format_list_for_display(list)
- config[:with_uri] ? list : list.keys.sort { |a, b| a <=> b }
- end
-
- def format_for_display(data)
- if formatting_subset_of_data?
- format_data_subset_for_display(data)
- elsif config[:id_only]
- name_or_id_for(data)
- elsif config[:environment] && data.respond_to?(:chef_environment)
- { "chef_environment" => data.chef_environment }
- else
- data
- end
- end
-
- def format_data_subset_for_display(data)
- subset = if config[:attribute]
- result = {}
- Array(config[:attribute]).each do |nested_value_spec|
- nested_value = extract_nested_value(data, nested_value_spec)
- result[nested_value_spec] = nested_value
- end
- result
- elsif config[:run_list]
- run_list = data.run_list.run_list
- { "run_list" => run_list }
- else
- raise ArgumentError, "format_data_subset_for_display requires attribute, run_list, or id_only config option to be set"
- end
- { name_or_id_for(data) => subset }
- end
-
- def name_or_id_for(data)
- data.respond_to?(:name) ? data.name : data["id"]
- end
-
- def formatting_subset_of_data?
- 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(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
- # 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] = {}
- versions["versions"].each do |ver|
- collected[cookbook][ver["version"]] = ver["url"]
- end
- collected
- end
- else
- versions_by_cookbook = item.inject({}) do |collected, ( cookbook, versions )|
- collected[cookbook] = versions["versions"].map { |v| v["version"] }
- collected
- end
- 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(" ")}"
- end
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/hashed_command_loader.rb b/lib/chef/knife/core/hashed_command_loader.rb
deleted file mode 100644
index c1d71f3edf..0000000000
--- a/lib/chef/knife/core/hashed_command_loader.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-# 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 "../../version"
-class Chef
- class Knife
- class SubcommandLoader
- #
- # Load a subcommand from a pre-computed path
- # for the given command.
- #
- class HashedCommandLoader < Chef::Knife::SubcommandLoader
- KEY = "_autogenerated_command_paths".freeze
-
- attr_accessor :manifest
-
- def initialize(chef_config_dir, plugin_manifest)
- super(chef_config_dir)
- @manifest = plugin_manifest
- end
-
- def guess_category(args)
- category_words = positional_arguments(args)
- category_words.map! { |w| w.split("-") }.flatten!
- find_longest_key(manifest[KEY]["plugins_by_category"], category_words, " ")
- end
-
- def list_commands(pref_category = nil)
- if pref_category || manifest[KEY]["plugins_by_category"].key?(pref_category)
- commands = { pref_category => manifest[KEY]["plugins_by_category"][pref_category] }
- else
- commands = manifest[KEY]["plugins_by_category"]
- end
- # 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 = {}
- commands.collect { |k, v| v }.flatten.each do |command|
- 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.exist?(sc) }
- errors[command] = paths
- end
- end
- end
- if errors.empty?
- commands
- else
- 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
- end
-
- def subcommand_files
- manifest[KEY]["plugins_paths"].values.flatten
- end
-
- def load_command(args)
- paths = manifest[KEY]["plugins_paths"][subcommand_for_args(args)]
- if paths.nil? || paths.empty? || (! paths.is_a? Array)
- false
- else
- paths.each do |sc|
- if File.exist?(sc)
- Kernel.load sc
- else
- return false
- end
- end
- true
- end
- end
-
- def subcommand_for_args(args)
- if manifest[KEY]["plugins_paths"].key?(args)
- args
- else
- find_longest_key(manifest[KEY]["plugins_paths"], args, "_")
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/node_editor.rb b/lib/chef/knife/core/node_editor.rb
deleted file mode 100644
index 2f9b326d16..0000000000
--- a/lib/chef/knife/core/node_editor.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Author:: Jordan Running (<jr@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 "../../node"
-
-class Chef
- class Knife
- class NodeEditor
- attr_reader :node, :ui, :config
- private :node, :ui, :config
-
- # @param node [Chef::Node]
- # @param ui [Chef::Knife::UI]
- # @param config [Hash]
- def initialize(node, ui, config)
- @node, @ui, @config = node, ui, config
- end
-
- # Opens the node data (as JSON) in the user's editor and returns a new
- # {Chef::Node} reflecting the user's changes.
- #
- # @return [Chef::Node]
- def edit_node
- abort "You specified the --disable_editing option, nothing to edit" if config[:disable_editing]
- assert_editor_set!
-
- updated_node_data = ui.edit_hash(view)
- apply_updates(updated_node_data)
- @updated_node
- end
-
- # Returns an array of the names of properties that have been changed or
- # +false+ if none were changed.
- #
- # @return [Array<String>] if any properties have been changed.
- # @return [false] if no properties have been changed.
- def updated?
- return false if @updated_node.nil?
-
- pristine_copy = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(node))
- updated_copy = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(@updated_node))
-
- updated_properties = %w{
- name
- chef_environment
- automatic
- default
- normal
- override
- policy_name
- policy_group
- run_list
- }.reject do |key|
- pristine_copy[key] == updated_copy[key]
- end
-
- updated_properties.any? && updated_properties
- end
-
- # @api private
- def view
- result = {
- "name" => node.name,
- "chef_environment" => node.chef_environment,
- "normal" => node.normal_attrs,
- "policy_name" => node.policy_name,
- "policy_group" => node.policy_group,
- "run_list" => node.run_list,
- }
-
- if config[:all_attributes]
- result["default"] = node.default_attrs
- result["override"] = node.override_attrs
- result["automatic"] = node.automatic_attrs
- end
-
- result
- end
-
- # @api private
- def apply_updates(updated_data)
- if node.name && node.name != updated_data["name"]
- ui.warn "Changing the name of a node results in a new node being created, #{node.name} will not be modified or removed."
- ui.confirm "Proceed with creation of new node"
- end
-
- data = updated_data.dup
-
- unless config[:all_attributes]
- data["automatic"] = node.automatic_attrs
- data["default"] = node.default_attrs
- data["override"] = node.override_attrs
- end
-
- @updated_node = Node.from_hash(data)
- end
-
- private
-
- def abort(message)
- ui.error(message)
- exit 1
- end
-
- def assert_editor_set!
- unless config[:editor]
- abort "You must set your EDITOR environment variable or configure your editor via knife.rb"
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/core/node_presenter.rb b/lib/chef/knife/core/node_presenter.rb
deleted file mode 100644
index 6690bc1075..0000000000
--- a/lib/chef/knife/core/node_presenter.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-#
-# 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 "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
- # @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
-
- # A customized presenter for Chef::Node objects. Supports variable-length
- # output formats for displaying node data
- class NodePresenter < GenericPresenter
-
- def format(data)
- if parse_format_option == :json
- summarize_json(data)
- else
- super
- end
- end
-
- def summarize_json(data)
- if data.is_a?(Chef::Node)
- node = data
- result = {}
-
- result["name"] = node.name
- if node.policy_name.nil? && node.policy_group.nil?
- result["chef_environment"] = node.chef_environment
- else
- result["policy_name"] = node.policy_name
- result["policy_group"] = node.policy_group
- end
- result["run_list"] = node.run_list
- result["normal"] = node.normal_attrs
-
- if config[:long_output]
- result["default"] = node.default_attrs
- result["override"] = node.override_attrs
- result["automatic"] = node.automatic_attrs
- end
-
- Chef::JSONCompat.to_json_pretty(result)
- else
- Chef::JSONCompat.to_json_pretty(data)
- end
- end
-
- # Converts a Chef::Node object to a string suitable for output to a
- # terminal. If config[:medium_output] or config[:long_output] are set
- # the volume of output is adjusted accordingly. Uses colors if enabled
- # in the ui object.
- def summarize(data)
- if data.is_a?(Chef::Node)
- node = data
- # 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
- 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
- else
- 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
- unless show_policy
- 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
- if config[:medium_output] || config[:long_output]
- 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
- end
- summarized
- else
- super
- end
- end
-
- def key(key_text)
- ui.color(key_text, :cyan)
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/object_loader.rb b/lib/chef/knife/core/object_loader.rb
deleted file mode 100644
index 5421fc46ce..0000000000
--- a/lib/chef/knife/core/object_loader.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-#
-# 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.
-#
-
-autoload :FFI_Yajl, "ffi_yajl"
-require_relative "../../util/path_helper"
-require_relative "../../data_bag_item"
-
-class Chef
- class Knife
- module Core
- class ObjectLoader
-
- attr_reader :ui
- attr_reader :klass
-
- class ObjectType
- FILE = 1
- FOLDER = 2
- end
-
- def initialize(klass, ui)
- @klass = klass
- @ui = ui
- end
-
- 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("/")}'"
- exit 1
- end
- object_from_file(object_file)
- end
-
- # When someone makes this awesome, please update the above error message.
- def find_file(repo_location, *components)
- if file_exists_and_is_readable?(File.expand_path( components.last ))
- File.expand_path( components.last )
- else
- relative_path = File.join(Dir.pwd, repo_location, *components)
- if file_exists_and_is_readable?(relative_path)
- relative_path
- else
- nil
- end
- end
- end
-
- # Find all objects in the given location
- # If the object type is File it will look for all *.{json,rb}
- # files, otherwise it will lookup for folders only (useful for
- # data_bags)
- #
- # @param [String] path - base look up location
- #
- # @return [Array<String>] basenames of the found objects
- #
- # @api public
- def find_all_objects(path)
- path = File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path(path)), "*")
- path << ".{json,rb}"
- objects = Dir.glob(path)
- objects.map { |o| File.basename(o) }
- end
-
- def find_all_object_dirs(path)
- path = File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path(path)), "*")
- objects = Dir.glob(path)
- objects.delete_if { |o| !File.directory?(o) }
- objects.map { |o| File.basename(o) }
- end
-
- def object_from_file(filename)
- case filename
- when /\.(js|json)$/
- r = FFI_Yajl::Parser.parse(IO.read(filename))
-
- # Chef::DataBagItem doesn't work well with the json_create method
- if @klass == Chef::DataBagItem
- r
- else
- @klass.from_hash(r)
- end
- when /\.rb$/
- r = klass.new
- r.from_file(filename)
- r
- else
- ui.fatal("File must end in .js, .json, or .rb")
- exit 30
- end
- end
-
- def file_exists_and_is_readable?(file)
- File.exist?(file) && File.readable?(file)
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/status_presenter.rb b/lib/chef/knife/core/status_presenter.rb
deleted file mode 100644
index 9a4ea76508..0000000000
--- a/lib/chef/knife/core/status_presenter.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-#
-# 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.
-#
-
-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
- # @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
-
- # A customized presenter for Chef::Node objects. Supports variable-length
- # output formats for displaying node data
- class StatusPresenter < GenericPresenter
-
- def format(data)
- if parse_format_option == :json
- summarize_json(data)
- else
- super
- end
- end
-
- def summarize_json(list)
- result_list = []
- list.each do |node|
- result = {}
-
- result["name"] = node["name"] || node.name
- result["chef_environment"] = node["chef_environment"]
- 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"]
- result["ohai_time"] = node["ohai_time"]
- result["platform"] = node["platform"] if node["platform"]
- result["platform_version"] = node["platform_version"] if node["platform_version"]
-
- if config[:long_output]
- result["default"] = node.default_attrs
- result["override"] = node.override_attrs
- result["automatic"] = node.automatic_attrs
- end
- result_list << result
- end
-
- Chef::JSONCompat.to_json_pretty(result_list)
- end
-
- # Converts a Chef::Node object to a string suitable for output to a
- # terminal. If config[:medium_output] or config[:long_output] are set
- # the volume of output is adjusted accordingly. Uses colors if enabled
- # in the ui object.
- def summarize(list)
- summarized = ""
- list.each do |data|
- node = data
- # 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
-
- 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
- line_parts << "Node #{name} has not yet converged"
- end
-
- line_parts << fqdn if fqdn
- line_parts << ip if ip
- line_parts << run_list.to_s if run_list
-
- if node["platform"]
- platform = node["platform"].dup
- if node["platform_version"]
- platform << " #{node["platform_version"]}"
- end
- line_parts << platform
- end
-
- summarized = summarized + line_parts.join(", ") + ".\n"
- end
- summarized
- end
-
- def key(key_text)
- ui.color(key_text, :cyan)
- end
-
- # @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
- hours = (difference / 3600).to_i
- difference = difference % 3600
- minutes = (difference / 60).to_i
- seconds = (difference % 60)
- [hours, minutes, seconds]
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
deleted file mode 100644
index 26d7e0277c..0000000000
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ /dev/null
@@ -1,203 +0,0 @@
-# Author:: Christopher Brown (<cb@chef.io>)
-# 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 "../../version"
-require_relative "../../util/path_helper"
-require_relative "gem_glob_loader"
-require_relative "hashed_command_loader"
-
-class Chef
- class Knife
- #
- # Public Methods of a Subcommand Loader
- #
- # load_commands - loads all available subcommands
- # load_command(args) - loads subcommands for the given args
- # list_commands(args) - lists all available subcommands,
- # optionally filtering by category
- # subcommand_files - returns an array of all subcommand files
- # that could be loaded
- # command_class_from(args) - returns the subcommand class for the
- # user-requested command
- #
- class SubcommandLoader
- attr_reader :chef_config_dir
-
- # A small factory method. Eventually, this is the only place
- # where SubcommandLoader should know about its subclasses, but
- # to maintain backwards compatibility many of the instance
- # methods in this base class contain default implementations
- # of the functions sub classes should otherwise provide
- # or directly instantiate the appropriate subclass
- def self.for_config(chef_config_dir)
- if autogenerated_manifest?
- Chef::Log.trace("Using autogenerated hashed command manifest #{plugin_manifest_path}")
- Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
- else
- Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
- end
- end
-
- # There are certain situations where we want to shortcut the loader selection
- # in self.for_config and force using the GemGlobLoader
- def self.gem_glob_loader(chef_config_dir)
- Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
- end
-
- def self.plugin_manifest?
- plugin_manifest_path && File.exist?(plugin_manifest_path)
- end
-
- def self.autogenerated_manifest?
- plugin_manifest? && plugin_manifest.key?(HashedCommandLoader::KEY)
- end
-
- def self.plugin_manifest
- Chef::JSONCompat.from_json(File.read(plugin_manifest_path))
- end
-
- def self.plugin_manifest_path
- Chef::Util::PathHelper.home(".chef", "plugin_manifest.json")
- end
-
- 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
-
- 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
-
- def force_load
- @loaded = false
- load_commands
- end
-
- def load_command(_command_args)
- load_commands
- end
-
- def list_commands(pref_cat = nil)
- load_commands
- if pref_cat && Chef::Knife.subcommands_by_category.key?(pref_cat)
- { pref_cat => Chef::Knife.subcommands_by_category[pref_cat] }
- else
- Chef::Knife.subcommands_by_category
- end
- end
-
- def command_class_from(args)
- cmd_words = positional_arguments(args)
- load_command(cmd_words)
- result = Chef::Knife.subcommands[find_longest_key(Chef::Knife.subcommands,
- cmd_words, "_")]
- result || Chef::Knife.subcommands[args.first.tr("-", "_")]
- end
-
- def guess_category(args)
- category_words = positional_arguments(args)
- category_words.map! { |w| w.split("-") }.flatten!
- find_longest_key(Chef::Knife.subcommands_by_category,
- category_words, " ")
- end
-
- #
- # This is shared between the custom_manifest_loader and the gem_glob_loader
- #
- 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", __dir__)), "*.rb")]
- subcommand_files = {}
- files.each do |knife_file|
- rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/, 1]
- subcommand_files[rel_path] = knife_file
- end
- subcommand_files
- 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).tr("-", "_")
- if hash.key?(candidate)
- match = candidate
- else
- words.pop
- end
- end
- match
- end
-
- #
- # The positional arguments from the argument list provided by the
- # users. Used to search for subcommands and categories.
- #
- # @return [Array<String>]
- #
- def positional_arguments(args)
- args.select { |arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
- end
-
- # Returns an Array of paths to knife commands located in
- # chef_config_dir/plugins/knife/ and ~/.chef/plugins/knife/
- def site_subcommands
- user_specific_files = []
-
- if chef_config_dir
- user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", Chef::Util::PathHelper.escape_glob_dir(chef_config_dir)))
- end
-
- # finally search ~/.chef/plugins/knife/*.rb
- Chef::Util::PathHelper.home(".chef", "plugins", "knife") do |p|
- user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(p), "*.rb"))
- end
-
- user_specific_files
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/text_formatter.rb b/lib/chef/knife/core/text_formatter.rb
deleted file mode 100644
index ec97748afb..0000000000
--- a/lib/chef/knife/core/text_formatter.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# 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.
-#
-
-class Chef
- class Knife
- module Core
- class TextFormatter
-
- attr_reader :data
- attr_reader :ui
-
- def initialize(data, ui)
- @ui = ui
- @data = if data.respond_to?(:display_hash)
- data.display_hash
- elsif data.is_a?(Array)
- data
- elsif data.respond_to?(:to_hash)
- data.to_hash
- else
- data
- end
- end
-
- def formatted_data
- @formatted_data ||= text_format(data)
- end
-
- def text_format(data)
- buffer = ""
-
- if data.respond_to?(:keys)
- 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.is_a?(Array) && value.size == 1 && is_singleton(value[0])
- value = value[0]
- end
- if is_singleton(value)
- # Strings are printed as key: value.
- justified_key = ui.color("#{key}:".ljust(justify_width), :cyan)
- buffer << "#{justified_key} #{value}\n"
- else
- # Arrays and hashes get indented on their own lines.
- buffer << ui.color("#{key}:\n", :cyan)
- lines = text_format(value).split("\n")
- lines.each { |line| buffer << " #{line}\n" }
- end
- end
- elsif data.is_a?(Array)
- data.each_index do |index|
- item = data[index]
- buffer << text_format(data[index])
- # Separate items with newlines if it's an array of hashes or an
- # array of arrays
- buffer << "\n" if !is_singleton(data[index]) && index != data.size - 1
- end
- else
- buffer << "#{data}\n"
- end
- buffer
- end
-
- def is_singleton(value)
- !(value.is_a?(Array) || value.respond_to?(:keys))
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/ui.rb b/lib/chef/knife/core/ui.rb
deleted file mode 100644
index aa84537064..0000000000
--- a/lib/chef/knife/core/ui.rb
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Brown (<cb@chef.io>)
-# 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 "forwardable" unless defined?(Forwardable)
-require_relative "../../platform/query_helpers"
-require_relative "generic_presenter"
-require "tempfile" unless defined?(Tempfile)
-
-class Chef
- class Knife
-
- # The User Interaction class used by knife.
- class UI
-
- extend Forwardable
-
- attr_reader :stdout
- attr_reader :stderr
- attr_reader :stdin
- attr_reader :config
-
- attr_reader :presenter
-
- def_delegator :@presenter, :format_list_for_display
- def_delegator :@presenter, :format_for_display
- def_delegator :@presenter, :format_cookbook_list_for_display
-
- def initialize(stdout, stderr, stdin, config)
- @stdout, @stderr, @stdin, @config = stdout, stderr, stdin, config
- @presenter = Chef::Knife::Core::GenericPresenter.new(self, config)
- end
-
- # Creates a new +presenter_class+ object and uses it to format structured
- # data for display. By default, a Chef::Knife::Core::GenericPresenter
- # object is used.
- def use_presenter(presenter_class)
- @presenter = presenter_class.new(self, config)
- end
-
- def highline
- @highline ||= begin
- require "highline"
- HighLine.new
- 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)
- 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)
- 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}")
- end
-
- # Print an error message
- #
- # @param message [String] the text string
- def error(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}")
- 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?
- pastel.decorate(string, *colors)
- else
- string
- end
- end
-
- # Should colored output be used? For output to a terminal, this is
- # determined by the value of `config[:color]`. When output is not to a
- # terminal, colored output is never used
- def color?
- Chef::Config[:color] && stdout.tty?
- end
-
- def ask(*args, **options, &block)
- prompt.ask(*args, **options, &block)
- end
-
- def list(*args)
- highline.list(*args)
- end
-
- # Formats +data+ using the configured presenter and outputs the result
- # via +msg+. Formatting can be customized by configuring a different
- # presenter. See +use_presenter+
- def output(data)
- msg @presenter.format(data)
- end
-
- # Determines if the output format is a data interchange format, i.e.,
- # JSON or YAML
- def interchange?
- @presenter.interchange?
- end
-
- def ask_question(question, opts = {})
- question += "[#{opts[:default]}] " if opts[:default]
-
- if opts[:default] && config[:defaults]
- opts[:default]
- else
- stdout.print question
- a = stdin.readline.strip
-
- if opts[:default]
- a.empty? ? opts[:default] : a
- else
- a
- end
- end
- end
-
- def pretty_print(data)
- stdout.puts data
- rescue Errno::EPIPE => e
- raise e if @config[:verbosity] >= 2
-
- exit 0
- end
-
- # Hash -> Hash
- # Works the same as edit_data but
- # returns a hash rather than a JSON string/Fully inflated object
- def edit_hash(hash)
- raw = edit_data(hash, false)
- Chef::JSONCompat.parse(raw)
- end
-
- def edit_data(data, parse_output = true, object_class: nil)
- output = Chef::JSONCompat.to_json_pretty(data)
- 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. See https://docs.chef.io/knife_setup/ for details." unless system("#{config[:editor]} #{tf.path}")
-
- output = IO.read(tf.path)
- end
- end
-
- if parse_output
- if object_class.nil?
- raise ArgumentError, "Please pass in the object class to hydrate or use #edit_hash"
- else
- object_class.from_hash(Chef::JSONCompat.parse(output))
- end
- else
- output
- end
- end
-
- def edit_object(klass, name)
- object = klass.load(name)
-
- output = edit_data(object, object_class: klass)
-
- # Only make the save if the user changed the object.
- #
- # Output JSON for the original (object) and edited (output), then parse
- # them without reconstituting the objects into real classes
- # (create_additions=false). Then, compare the resulting simple objects,
- # which will be Array/Hash/String/etc.
- #
- # We wouldn't have to do these shenanigans if all the editable objects
- # implemented to_hash, or if to_json against a hash returned a string
- # with stable key order.
- object_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(object))
- output_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(output))
- if object_parsed_again != output_parsed_again
- output.save
- msg("Saved #{output}")
- else
- msg("Object unchanged, not saving")
- end
- output(format_for_display(object)) if config[:print_after]
- end
-
- def confirmation_instructions(default_choice)
- case default_choice
- when true
- "? (Y/n) "
- when false
- "? (y/N) "
- else
- "? (Y/N) "
- end
- end
-
- # See confirm method for argument information
- def confirm_without_exit(question, append_instructions = true, default_choice = nil)
- return true if config[:yes]
-
- stdout.print question
- stdout.print confirmation_instructions(default_choice) if append_instructions
-
- answer = stdin.readline
- answer.chomp!
-
- case answer
- when "Y", "y"
- true
- when "N", "n"
- msg("You said no, so I'm done here.")
- false
- when ""
- unless default_choice.nil?
- default_choice
- else
- 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
- 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
-
- #
- # Not the ideal signature for a function but we need to stick with this
- # for now until we get a chance to break our API in Chef 12.
- #
- # question => Question to print before asking for confirmation
- # append_instructions => Should print '? (Y/N)' as instructions
- # default_choice => Set to true for 'Y', and false for 'N' as default answer
- #
- def confirm(question, append_instructions = true, default_choice = nil)
- unless confirm_without_exit(question, append_instructions, default_choice)
- exit 3
- end
- true
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/core/windows_bootstrap_context.rb b/lib/chef/knife/core/windows_bootstrap_context.rb
deleted file mode 100644
index 4b40d5bfb9..0000000000
--- a/lib/chef/knife/core/windows_bootstrap_context.rb
+++ /dev/null
@@ -1,406 +0,0 @@
-#
-# 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::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
deleted file mode 100644
index 11448c69b7..0000000000
--- a/lib/chef/knife/data_bag_create.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Seth Falcon (<seth@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"
-require_relative "data_bag_secret_options"
-
-class Chef
- class Knife
- class DataBagCreate < Knife
- include DataBagSecretOptions
-
- deps do
- require_relative "../data_bag"
- require_relative "../encrypted_data_bag_item"
- end
-
- banner "knife data bag create BAG [ITEM] (options)"
- category "data bag"
-
- def run
- @data_bag_name, @data_bag_item_name = @name_args
-
- if @data_bag_name.nil?
- show_usage
- ui.fatal("You must specify a data bag name")
- exit 1
- end
-
- begin
- Chef::DataBag.validate_name!(@data_bag_name)
- rescue Chef::Exceptions::InvalidDataBagName => e
- ui.fatal(e.message)
- exit(1)
- end
-
- # 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}]")
- end
-
- # if an item is specified, create it, as well
- if @data_bag_item_name
- create_object({ "id" => @data_bag_item_name }, "data_bag_item[#{@data_bag_item_name}]") do |output|
- item = Chef::DataBagItem.from_hash(
- if encryption_secret_provided?
- Chef::EncryptedDataBagItem.encrypt_data_bag_item(output, read_secret)
- else
- output
- end
- )
- item.data_bag(@data_bag_name)
- rest.post("data/#{@data_bag_name}", item)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_delete.rb b/lib/chef/knife/data_bag_delete.rb
deleted file mode 100644
index ab38244e91..0000000000
--- a/lib/chef/knife/data_bag_delete.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 DataBagDelete < Knife
-
- deps do
- require_relative "../data_bag"
- end
-
- banner "knife data bag delete BAG [ITEM] (options)"
- category "data bag"
-
- def run
- if @name_args.length == 2
- delete_object(Chef::DataBagItem, @name_args[1], "data_bag_item") do
- rest.delete("data/#{@name_args[0]}/#{@name_args[1]}")
- end
- elsif @name_args.length == 1
- delete_object(Chef::DataBag, @name_args[0], "data_bag") do
- rest.delete("data/#{@name_args[0]}")
- end
- else
- show_usage
- ui.fatal("You must specify at least a data bag name")
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_edit.rb b/lib/chef/knife/data_bag_edit.rb
deleted file mode 100644
index 1935f2149e..0000000000
--- a/lib/chef/knife/data_bag_edit.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Seth Falcon (<seth@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"
-require_relative "data_bag_secret_options"
-
-class Chef
- class Knife
- class DataBagEdit < Knife
- include DataBagSecretOptions
-
- deps do
- require_relative "../data_bag_item"
- require_relative "../encrypted_data_bag_item"
- end
-
- banner "knife data bag edit BAG ITEM (options)"
- category "data bag"
-
- def load_item(bag, item_name)
- item = Chef::DataBagItem.load(bag, item_name)
- if encrypted?(item.raw_data)
- if encryption_secret_provided_ignore_encrypt_flag?
- [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
- [item.raw_data, false]
- end
- end
-
- def run
- if @name_args.length != 2
- stdout.puts "You must supply the data bag and an item to edit"
- stdout.puts opt_parser
- exit 1
- end
-
- item, was_encrypted = load_item(@name_args[0], @name_args[1])
- edited_item = edit_hash(item)
-
- if was_encrypted || encryption_secret_provided?
- ui.info("Encrypting data bag using provided secret.")
- item_to_save = Chef::EncryptedDataBagItem.encrypt_data_bag_item(edited_item, read_secret)
- else
- ui.info("Saving data bag unencrypted. To encrypt it, provide an appropriate secret.")
- item_to_save = edited_item
- end
-
- rest.put("data/#{@name_args[0]}/#{@name_args[1]}", item_to_save)
- stdout.puts("Saved data_bag_item[#{@name_args[1]}]")
- ui.output(edited_item) if config[:print_after]
- end
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_from_file.rb b/lib/chef/knife/data_bag_from_file.rb
deleted file mode 100644
index 5f48b0a934..0000000000
--- a/lib/chef/knife/data_bag_from_file.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Seth Falcon (<seth@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"
-require_relative "data_bag_secret_options"
-
-class Chef
- class Knife
- class DataBagFromFile < Knife
- include DataBagSecretOptions
-
- deps do
- 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."
-
- def loader
- @loader ||= Knife::Core::ObjectLoader.new(DataBagItem, ui)
- end
-
- def run
- if config[:all] == true
- load_all_data_bags(@name_args)
- else
- if @name_args.size < 2
- ui.msg(opt_parser)
- exit(1)
- end
- @data_bag = @name_args.shift
- load_data_bag_items(@data_bag, @name_args)
- end
- end
-
- private
-
- def data_bags_path
- @data_bag_path ||= "data_bags"
- end
-
- def find_all_data_bags
- loader.find_all_object_dirs("./#{data_bags_path}")
- end
-
- def find_all_data_bag_items(data_bag)
- loader.find_all_objects("./#{data_bags_path}/#{data_bag}")
- end
-
- def load_all_data_bags(args)
- data_bags = args.empty? ? find_all_data_bags : [args.shift]
- data_bags.each do |data_bag|
- load_data_bag_items(data_bag)
- end
- end
-
- def load_data_bag_items(data_bag, items = nil)
- 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).to_s, data_bag, item_path)
- item = if encryption_secret_provided?
- Chef::EncryptedDataBagItem.encrypt_data_bag_item(item, read_secret)
- else
- item
- end
- dbag = Chef::DataBagItem.new
- dbag.data_bag(data_bag)
- dbag.raw_data = item
- dbag.save
- ui.info("Updated data_bag_item[#{dbag.data_bag}::#{dbag.id}]")
- end
- end
-
- def normalize_item_paths(args)
- paths = []
- args.each do |path|
- if File.directory?(path)
- paths.concat(Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(path), "*.json")))
- else
- paths << path
- end
- end
- paths
- end
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_list.rb b/lib/chef/knife/data_bag_list.rb
deleted file mode 100644
index 801bf588b4..0000000000
--- a/lib/chef/knife/data_bag_list.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 DataBagList < Knife
-
- deps do
- 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."
-
- def run
- output(format_list_for_display(Chef::DataBag.list))
- end
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_secret_options.rb b/lib/chef/knife/data_bag_secret_options.rb
deleted file mode 100644
index 8f9f96502f..0000000000
--- a/lib/chef/knife/data_bag_secret_options.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "mixlib/cli" unless defined?(Mixlib::CLI)
-require_relative "../config"
-require_relative "../encrypted_data_bag_item/check_encrypted"
-
-class Chef
- class Knife
- module DataBagSecretOptions
- include Mixlib::CLI
- include Chef::EncryptedDataBagItem::CheckEncrypted
-
- # The config object is populated by knife#merge_configs with knife.rb `knife[:*]` config values, but they do
- # not overwrite the command line properties. It does mean, however, that `knife[:secret]` and `--secret-file`
- # passed at the same time populate both `config[:secret]` and `config[:secret_file]`. We cannot differentiate
- # the valid case (`knife[:secret]` in config file and `--secret-file` on CL) and the invalid case (`--secret`
- # and `--secret-file` on the CL) - thats why I'm storing the CL options in a different config key if they
- # are provided.
-
- def self.included(base)
- 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
- end
-
- def encryption_secret_provided?
- base_encryption_secret_provided?
- end
-
- def encryption_secret_provided_ignore_encrypt_flag?
- base_encryption_secret_provided?(false)
- end
-
- 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_relative "../encrypted_data_bag_item"
-
- 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 = config[:secret_file]
- Chef::EncryptedDataBagItem.load_secret(secret_file)
- end
- end
-
- def validate_secrets
- if config[:cl_secret] && config[:cl_secret_file]
- ui.fatal("Please specify only one of --secret, --secret-file")
- exit(1)
- end
-
- if config[:secret] && config[:secret_file]
- ui.fatal("Please specify only one of 'secret' or 'secret_file' in your config file")
- exit(1)
- end
- end
-
- private
-
- ##
- # Determine if the user has specified an appropriate secret for encrypting data bag items.
- # @return boolean
- def base_encryption_secret_provided?(need_encrypt_flag = true)
- validate_secrets
-
- return true if config[:cl_secret] || config[:cl_secret_file]
-
- if need_encrypt_flag
- if config[:encrypt]
- 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 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
- 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
-
- end
- end
-end
diff --git a/lib/chef/knife/data_bag_show.rb b/lib/chef/knife/data_bag_show.rb
deleted file mode 100644
index cb7b56c333..0000000000
--- a/lib/chef/knife/data_bag_show.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Seth Falcon (<seth@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"
-require_relative "data_bag_secret_options"
-
-class Chef
- class Knife
- class DataBagShow < Knife
- include DataBagSecretOptions
-
- deps do
- require_relative "../data_bag"
- require_relative "../encrypted_data_bag_item"
- end
-
- banner "knife data bag show BAG [ITEM] (options)"
- category "data bag"
-
- def run
- display = case @name_args.length
- when 2 # Bag and Item names provided
- secret = encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil
- raw_data = Chef::DataBagItem.load(@name_args[0], @name_args[1]).raw_data
- encrypted = encrypted?(raw_data)
-
- if encrypted && secret
- # 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_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.") if secret
- format_for_display(raw_data)
- end
-
- when 1 # Only Bag name provided
- format_list_for_display(Chef::DataBag.load(@name_args[0]))
- else
- stdout.puts opt_parser
- exit(1)
- end
- output(display)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/delete.rb b/lib/chef/knife/delete.rb
deleted file mode 100644
index 3e5c545017..0000000000
--- a/lib/chef/knife/delete.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Delete < Chef::ChefFS::Knife
- banner "knife delete [PATTERN1 ... PATTERNn]"
-
- category "path-based"
-
- deps do
- require_relative "../chef_fs/file_system"
- end
-
- option :recurse,
- 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."
-
- option :local,
- long: "--local",
- boolean: true,
- default: false,
- description: "Delete the local copy (leave the remote copy)."
-
- def run
- if name_args.length == 0
- show_usage
- ui.fatal("You must specify at least one argument. If you want to delete everything in this directory, run \"knife delete --recurse .\"")
- exit 1
- end
-
- # Get the matches (recursively)
- error = false
- if config[:local]
- pattern_args.each do |pattern|
- Chef::ChefFS::FileSystem.list(local_fs, pattern).each do |result|
- if delete_result(result)
- error = true
- end
- end
- end
- elsif config[:both]
- pattern_args.each do |pattern|
- Chef::ChefFS::FileSystem.list_pairs(pattern, chef_fs, local_fs).each do |chef_result, local_result|
- if delete_result(chef_result, local_result)
- error = true
- end
- end
- end
- else # Remote only
- pattern_args.each do |pattern|
- Chef::ChefFS::FileSystem.list(chef_fs, pattern).each do |result|
- if delete_result(result)
- error = true
- end
- end
- end
- end
-
- if error
- exit 1
- end
- end
-
- def format_path_with_root(entry)
- root = entry.root == chef_fs ? " (remote)" : " (local)"
- "#{format_path(entry)}#{root}"
- end
-
- def delete_result(*results)
- deleted_any = false
- found_any = false
- error = false
- results.each do |result|
-
- 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])}")
- elsif !found_any
- ui.error "#{format_path(results[0])}: No such file or directory"
- error = true
- end
- error
- end
- end
- end
-end
diff --git a/lib/chef/knife/deps.rb b/lib/chef/knife/deps.rb
deleted file mode 100644
index f620b53bfa..0000000000
--- a/lib/chef/knife/deps.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Deps < Chef::ChefFS::Knife
- banner "knife deps PATTERN1 [PATTERNn]"
-
- category "path-based"
-
- deps do
- 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."
-
- option :tree,
- 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."
-
- attr_accessor :exit_code
-
- def run
- if config[:recurse] == false && !config[:tree]
- ui.error "--no-recurse requires --tree"
- exit(1)
- end
- config[:recurse] = true if config[:recurse].nil?
-
- @root = config[:remote] ? chef_fs : local_fs
- dependencies = {}
- pattern_args.each do |pattern|
- Chef::ChefFS::FileSystem.list(@root, pattern).each do |entry|
- if config[:tree]
- print_dependencies_tree(entry, dependencies)
- else
- print_flattened_dependencies(entry, dependencies)
- end
- end
- end
- exit exit_code if exit_code
- end
-
- def print_flattened_dependencies(entry, dependencies)
- 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)
- print_flattened_dependencies(child_entry, dependencies)
- end
- output format_path(entry)
- end
- end
-
- def print_dependencies_tree(entry, dependencies, printed = {}, depth = 0)
- 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|
- child_entry = Chef::ChefFS::FileSystem.resolve_path(@root, child)
- print_dependencies_tree(child_entry, dependencies, printed, depth + 1)
- end
- end
- end
-
- def get_dependencies(entry)
- 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
- 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
- end
- result
-
- 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
- []
- end
-
- def dependencies_from_runlist(run_list)
- chef_run_list = Chef::RunList.new
- chef_run_list.reset!(run_list)
- chef_run_list.map do |run_list_item|
- case run_list_item.type
- when :role
- "/roles/#{run_list_item.name}.json"
- when :recipe
- if run_list_item.name =~ /(.+)::[^:]*/
- "/cookbooks/#{$1}"
- else
- "/cookbooks/#{run_list_item.name}"
- end
- else
- raise "Unknown run list item type #{run_list_item.type}"
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/diff.rb b/lib/chef/knife/diff.rb
deleted file mode 100644
index 3e9336aacc..0000000000
--- a/lib/chef/knife/diff.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Diff < Chef::ChefFS::Knife
- banner "knife diff PATTERNS"
-
- category "path-based"
-
- deps do
- require_relative "../chef_fs/command_line"
- end
-
- option :recurse,
- 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."
-
- option :name_status,
- 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."
-
- option :cookbook_version,
- 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]
- output_mode = :name_only
- end
- if config[:name_status]
- output_mode = :name_status
- end
- patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ])
-
- # Get the matches (recursively)
- error = false
- begin
- patterns.each do |pattern|
- found_error = Chef::ChefFS::CommandLine.diff_print(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, output_mode, proc { |entry| format_path(entry) }, config[:diff_filter], ui ) do |diff|
- stdout.print diff
- end
- error = true if found_error
- end
- rescue Chef::ChefFS::FileSystem::OperationFailedError => e
- ui.error "Failed on #{format_path(e.entry)} in #{e.operation}: #{e.message}"
- error = true
- end
-
- if error
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/download.rb b/lib/chef/knife/download.rb
deleted file mode 100644
index ab8c92a1c0..0000000000
--- a/lib/chef/knife/download.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Download < Chef::ChefFS::Knife
- banner "knife download PATTERNS"
-
- category "path-based"
-
- deps do
- require_relative "../chef_fs/command_line"
- end
-
- option :recurse,
- 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."
-
- option :force,
- 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."
-
- option :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)."
-
- def run
- if name_args.length == 0
- show_usage
- ui.fatal("You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"")
- exit 1
- end
-
- error = false
- pattern_args.each do |pattern|
- if Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, config, ui, proc { |entry| format_path(entry) })
- error = true
- end
- end
- if error
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/edit.rb b/lib/chef/knife/edit.rb
deleted file mode 100644
index caca201566..0000000000
--- a/lib/chef/knife/edit.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Edit < Chef::ChefFS::Knife
- banner "knife edit [PATTERN1 ... PATTERNn]"
-
- category "path-based"
-
- deps do
- 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."
-
- def run
- # Get the matches (recursively)
- error = false
- pattern_args.each do |pattern|
- Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result|
- if result.dir?
- ui.error "#{format_path(result)}: is a directory" if pattern.exact_path
- error = true
- else
- begin
- new_value = edit_text(result.read, File.extname(result.name))
- if new_value
- result.write(new_value)
- output "Updated #{format_path(result)}"
- else
- output "#{format_path(result)} unchanged"
- end
- rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
- ui.error "#{format_path(e.entry)}: #{e.reason}."
- error = true
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- error = true
- end
- end
- end
- end
- if error
- exit 1
- end
- end
-
- def edit_text(text, extension)
- 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
- 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)
-
- return result_text if result_text != text
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_compare.rb b/lib/chef/knife/environment_compare.rb
deleted file mode 100644
index 22abee59c8..0000000000
--- a/lib/chef/knife/environment_compare.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# Author:: Sander Botman (<sbotman@schubergphilis.com>)
-# Copyright:: Copyright 2013-2016, Sander Botman.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 EnvironmentCompare < Knife
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment compare [ENVIRONMENT..] (options)"
-
- option :all,
- short: "-a",
- long: "--all",
- description: "Show all cookbooks.",
- boolean: true
-
- option :mismatch,
- short: "-m",
- long: "--mismatch",
- description: "Only show mismatching versions.",
- boolean: true
-
- def run
- # Get the commandline environments or all if none are provided.
- environments = environment_list
-
- # Get a list of all cookbooks that have constraints and their environment.
- constraints = constraint_list(environments)
-
- # Get the total list of cookbooks that have constraints
- cookbooks = cookbook_list(constraints)
-
- # If we cannot find any cookbooks, we can stop here.
- if cookbooks.nil? || cookbooks.empty?
- ui.error "Cannot find any environment cookbook constraints"
- exit 1
- end
-
- # Get all cookbooks so we can compare them all
- cookbooks = rest.get("/cookbooks?num_versions=1") if config[:all]
-
- # display matrix view of in the requested format.
- if config[:format] == "summary"
- matrix = matrix_output(cookbooks, constraints)
- ui.output(matrix)
- else
- ui.output(constraints)
- end
- end
-
- private
-
- def environment_list
- environments = []
- unless @name_args.nil? || @name_args.empty?
- @name_args.each { |name| environments << name }
- else
- environments = Chef::Environment.list
- end
- end
-
- def constraint_list(environments)
- constraints = {}
- 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)
- ver = envdata.cookbook_versions
- constraints[env] = ver
- end
- end
- constraints
- end
-
- def cookbook_list(constraints)
- result = {}
- constraints.each_value { |cb| result.merge!(cb) }
- result
- end
-
- def matrix_output(cookbooks, constraints)
- rows = [ "" ]
- environments = []
- constraints.each_key { |e| environments << e.to_s }
- columns = environments.count + 1
- environments.each { |env| rows << ui.color(env, :bold) }
- 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
- end
- rows << ui.color(c, :bold)
- environments.each do |e|
- tag = constraints[e][c] || "latest"
- rows << ui.color(tag, color)
- end
- end
- ui.list(rows, :uneven_columns_across, columns)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/environment_create.rb b/lib/chef/knife/environment_create.rb
deleted file mode 100644
index a724f72d4f..0000000000
--- a/lib/chef/knife/environment_create.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentCreate < Knife
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment create ENVIRONMENT (options)"
-
- option :description,
- short: "-d DESCRIPTION",
- long: "--description DESCRIPTION",
- description: "The environment description."
-
- def run
- env_name = @name_args[0]
-
- if env_name.nil?
- show_usage
- ui.fatal("You must specify an environment name")
- exit 1
- end
-
- env = Chef::Environment.new
- env.name(env_name)
- env.description(config[:description]) if config[:description]
- create_object(env, object_class: Chef::Environment)
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_delete.rb b/lib/chef/knife/environment_delete.rb
deleted file mode 100644
index ec1b7cb8d8..0000000000
--- a/lib/chef/knife/environment_delete.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentDelete < Knife
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment delete ENVIRONMENT (options)"
-
- def run
- env_name = @name_args[0]
-
- if env_name.nil?
- show_usage
- ui.fatal("You must specify an environment name")
- exit 1
- end
-
- delete_object(Chef::Environment, env_name)
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_edit.rb b/lib/chef/knife/environment_edit.rb
deleted file mode 100644
index 7c6105a6c0..0000000000
--- a/lib/chef/knife/environment_edit.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentEdit < Knife
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment edit ENVIRONMENT (options)"
-
- def run
- env_name = @name_args[0]
-
- if env_name.nil?
- show_usage
- ui.fatal("You must specify an environment name")
- exit 1
- end
-
- edit_object(Chef::Environment, env_name)
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_from_file.rb b/lib/chef/knife/environment_from_file.rb
deleted file mode 100644
index a5011a3abf..0000000000
--- a/lib/chef/knife/environment_from_file.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentFromFile < Knife
-
- deps do
- 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."
-
- def loader
- @loader ||= Knife::Core::ObjectLoader.new(Chef::Environment, ui)
- end
-
- def environments_path
- @environments_path ||= "environments"
- end
-
- def find_all_environments
- loader.find_all_objects("./#{environments_path}/")
- end
-
- def load_all_environments
- environments = find_all_environments
- if environments.empty?
- ui.fatal("Unable to find any environment files in '#{environments_path}'")
- exit(1)
- end
- environments.each do |env|
- load_environment(env)
- end
- end
-
- def load_environment(env)
- updated = loader.load_from("environments", env)
- updated.save
- output(format_for_display(updated)) if config[:print_after]
- ui.info("Updated Environment #{updated.name}")
- end
-
- def run
- if config[:all] == true
- load_all_environments
- else
- if @name_args[0].nil?
- show_usage
- ui.fatal("You must specify a file to load")
- exit 1
- end
-
- @name_args.each do |arg|
- load_environment(arg)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_list.rb b/lib/chef/knife/environment_list.rb
deleted file mode 100644
index 7bcdeb6084..0000000000
--- a/lib/chef/knife/environment_list.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentList < Knife
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment list (options)"
-
- option :with_uri,
- short: "-w",
- long: "--with-uri",
- description: "Show corresponding URIs."
-
- def run
- output(format_list_for_display(Chef::Environment.list))
- end
- end
- end
-end
diff --git a/lib/chef/knife/environment_show.rb b/lib/chef/knife/environment_show.rb
deleted file mode 100644
index e336b2d392..0000000000
--- a/lib/chef/knife/environment_show.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- class EnvironmentShow < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require_relative "../environment"
- end
-
- banner "knife environment show ENVIRONMENT (options)"
-
- def run
- env_name = @name_args[0]
-
- if env_name.nil?
- show_usage
- ui.fatal("You must specify an environment name")
- exit 1
- end
-
- env = Chef::Environment.load(env_name)
- output(format_for_display(env))
- end
- end
- end
-end
diff --git a/lib/chef/knife/exec.rb b/lib/chef/knife/exec.rb
deleted file mode 100644
index d3ce2cee24..0000000000
--- a/lib/chef/knife/exec.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-#--
-# 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 "../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 #{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(":") }
-
- deps do
- require_relative "../shell/ext"
- end
-
- def run
- 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
- Chef::Util::PathHelper.home(".chef", "scripts") { |p| config[:script_path] << p }
-
- scripts = Array(name_args)
- context = Object.new
- Shell::Extensions.extend_context_object(context)
- if config[:exec]
- context.instance_eval(config[:exec], "-E Argument", 0)
- elsif !scripts.empty?
- scripts.each do |script|
- file = find_script(script)
- 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
- end
-
- def find_script(x)
- # Try to find a script. First try expanding the path given.
- script = File.expand_path(x)
- return script if File.exist?(script)
-
- # Failing that, try searching the script path. If we can't find
- # anything, fail gracefully.
- 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.trace("Testing: #{test}")
- if File.exist?(test)
- script = test
- Chef::Log.trace("Found: #{test}")
- return script
- end
- end
- ui.error("\"#{x}\" not found in current directory or script_path, giving up.")
- exit(1)
- end
-
-end
diff --git a/lib/chef/knife/group_add.rb b/lib/chef/knife/group_add.rb
deleted file mode 100644
index eccb7dd10c..0000000000
--- a/lib/chef/knife/group_add.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# 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
deleted file mode 100644
index 4219188951..0000000000
--- a/lib/chef/knife/group_create.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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
deleted file mode 100644
index 433a5cc627..0000000000
--- a/lib/chef/knife/group_destroy.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# 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
deleted file mode 100644
index fc8f00ad6d..0000000000
--- a/lib/chef/knife/group_list.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# 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
deleted file mode 100644
index 07ab19693f..0000000000
--- a/lib/chef/knife/group_remove.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# 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
deleted file mode 100644
index 6ac53f6b6e..0000000000
--- a/lib/chef/knife/group_show.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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/key_create.rb b/lib/chef/knife/key_create.rb
deleted file mode 100644
index 6129cab683..0000000000
--- a/lib/chef/knife/key_create.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# 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_relative "../key"
-require_relative "../json_compat"
-require_relative "../exceptions"
-
-class Chef
- class Knife
- # Service class for UserKeyCreate and ClientKeyCreate,
- # Implements common functionality of knife [user | org client] key create.
- #
- # @author Tyler Cloke
- #
- # @attr_accessor [Hash] cli input, see UserKeyCreate and ClientKeyCreate for what could populate it
- class KeyCreate
-
- attr_accessor :config
-
- def initialize(actor, actor_field_name, ui, config)
- @actor = actor
- @actor_field_name = actor_field_name
- @ui = ui
- @config = config
- 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
- end
-
- def edit_data(key)
- @ui.edit_data(key)
- end
-
- def edit_hash(key)
- @ui.edit_hash(key)
- end
-
- def display_info(input)
- @ui.info(input)
- end
-
- def display_private_key(private_key)
- @ui.msg(private_key)
- end
-
- def output_private_key_to_file(private_key)
- File.open(@config[:file], "w") do |f|
- f.print(private_key)
- end
- end
-
- def create_key_from_hash(output)
- Chef::Key.from_hash(output).create
- end
-
- def run
- key = Chef::Key.new(@actor, @actor_field_name)
- if !@config[:public_key] && !@config[:key_name]
- raise Chef::Exceptions::KeyCommandInputError, public_key_or_key_name_error_msg
- elsif !@config[:public_key]
- key.create_key(true)
- end
-
- if @config[:public_key]
- key.public_key(File.read(File.expand_path(@config[:public_key])))
- end
-
- if @config[:key_name]
- key.name(@config[:key_name])
- end
-
- if @config[:expiration_date]
- key.expiration_date(@config[:expiration_date])
- else
- key.expiration_date("infinity")
- end
-
- output = edit_hash(key)
- key = create_key_from_hash(output)
-
- display_info("Created key: #{key.name}")
- if key.private_key
- if @config[:file]
- output_private_key_to_file(key.private_key)
- else
- display_private_key(key.private_key)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/key_create_base.rb b/lib/chef/knife/key_create_base.rb
deleted file mode 100644
index a1d658e43c..0000000000
--- a/lib/chef/knife/key_create_base.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# 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.
-#
-
-class Chef
- class Knife
- # Extendable module that class_eval's common options into UserKeyCreate and ClientKeyCreate
- #
- # @author Tyler Cloke
- module KeyCreateBase
- 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."
-
- option :file,
- 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."
-
- option :expiration_date,
- 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
- end
-end
diff --git a/lib/chef/knife/key_delete.rb b/lib/chef/knife/key_delete.rb
deleted file mode 100644
index 10f1235924..0000000000
--- a/lib/chef/knife/key_delete.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# 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_relative "../key"
-
-class Chef
- class Knife
- # Service class for UserKeyDelete and ClientKeyDelete, used to delete keys.
- # Implements common functionality of knife [user | org client] key delete.
- #
- # @author Tyler Cloke
- #
- # @attr_accessor [Hash] cli input, see UserKeyDelete and ClientKeyDelete for what could populate it
- class KeyDelete
- def initialize(name, actor, actor_field_name, ui)
- @name = name
- @actor = actor
- @actor_field_name = actor_field_name
- @ui = ui
- end
-
- def confirm!
- @ui.confirm("Do you really want to delete the key named #{@name} for the #{@actor_field_name} named #{@actor}")
- end
-
- def print_destroyed
- @ui.info("Deleted key named #{@name} for the #{@actor_field_name} named #{@actor}")
- end
-
- def run
- key = Chef::Key.new(@actor, @actor_field_name)
- key.name(@name)
- confirm!
- key.destroy
- print_destroyed
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/key_edit.rb b/lib/chef/knife/key_edit.rb
deleted file mode 100644
index 3f8918f1a9..0000000000
--- a/lib/chef/knife/key_edit.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# 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_relative "../key"
-require_relative "../json_compat"
-require_relative "../exceptions"
-
-class Chef
- class Knife
- # Service class for UserKeyEdit and ClientKeyEdit,
- # Implements common functionality of knife [user | org client] key edit.
- #
- # @author Tyler Cloke
- #
- # @attr_accessor [Hash] cli input, see UserKeyEdit and ClientKeyEdit for what could populate it
- class KeyEdit
-
- attr_accessor :config
-
- def initialize(original_name, actor, actor_field_name, ui, config)
- @original_name = original_name
- @actor = actor
- @actor_field_name = actor_field_name
- @ui = ui
- @config = config
- 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
- end
-
- def edit_data(key)
- @ui.edit_data(key)
- end
-
- def edit_hash(key)
- @ui.edit_hash(key)
- end
-
- def display_info(input)
- @ui.info(input)
- end
-
- def display_private_key(private_key)
- @ui.msg(private_key)
- end
-
- def output_private_key_to_file(private_key)
- File.open(@config[:file], "w") do |f|
- f.print(private_key)
- end
- end
-
- def update_key_from_hash(output)
- Chef::Key.from_hash(output).update(@original_name)
- end
-
- def run
- key = Chef::Key.new(@actor, @actor_field_name)
- if @config[:public_key] && @config[:create_key]
- raise Chef::Exceptions::KeyCommandInputError, public_key_and_create_key_error_msg
- end
-
- if @config[:create_key]
- key.create_key(true)
- end
-
- if @config[:public_key]
- key.public_key(File.read(File.expand_path(@config[:public_key])))
- end
-
- if @config[:key_name]
- key.name(@config[:key_name])
- else
- key.name(@original_name)
- end
-
- if @config[:expiration_date]
- key.expiration_date(@config[:expiration_date])
- end
-
- output = edit_hash(key)
- key = update_key_from_hash(output)
-
- to_display = "Updated key: #{key.name}"
- to_display << " (formally #{@original_name})" if key.name != @original_name
- display_info(to_display)
- if key.private_key
- if @config[:file]
- output_private_key_to_file(key.private_key)
- else
- display_private_key(key.private_key)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/key_edit_base.rb b/lib/chef/knife/key_edit_base.rb
deleted file mode 100644
index b094877190..0000000000
--- a/lib/chef/knife/key_edit_base.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# 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.
-#
-
-class Chef
- class Knife
- # Extendable module that class_eval's common options into UserKeyEdit and ClientKeyEdit
- #
- # @author Tyler Cloke
- module KeyEditBase
- 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."
-
- 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."
-
- 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."
-
- 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."
-
- 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 formatted string: YYYY-MM-DDTHH:MM:SSZ e.g. 2013-12-24T21:00:00Z or infinity. UTC timezone assumed."
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/key_list.rb b/lib/chef/knife/key_list.rb
deleted file mode 100644
index 076b39d251..0000000000
--- a/lib/chef/knife/key_list.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# 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_relative "../key"
-require_relative "../json_compat"
-require_relative "../exceptions"
-
-class Chef
- class Knife
- # Service class for UserKeyList and ClientKeyList, used to list keys.
- # Implements common functionality of knife [user | org client] key list.
- #
- # @author Tyler Cloke
- #
- # @attr_accessor [Hash] cli input, see UserKeyList and ClientKeyList for what could populate it
- class KeyList
-
- attr_accessor :config
-
- def initialize(actor, list_method, ui, config)
- @actor = actor
- @list_method = list_method
- @ui = ui
- @config = config
- end
-
- def expired_and_non_expired_msg
- <<~EOS
- You cannot pass both --only-expired and --only-non-expired.
- Please pass one or none.
- EOS
- end
-
- def display_info(string)
- @ui.output(string)
- end
-
- def colorize(string)
- @ui.color(string, :cyan)
- end
-
- def run
- if @config[:only_expired] && @config[:only_non_expired]
- raise Chef::Exceptions::KeyCommandInputError, expired_and_non_expired_msg
- end
-
- # call proper list function
- keys = Chef::Key.send(@list_method, @actor)
- if @config[:with_details]
- max_length = 0
- keys.each do |key|
- key["name"] = key["name"] + ":"
- max_length = key["name"].length if key["name"].length > max_length
- end
- 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 = "#{display} (expired)" if key["expired"]
- display_info(display)
- end
- else
- 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
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/key_list_base.rb b/lib/chef/knife/key_list_base.rb
deleted file mode 100644
index e06e908b69..0000000000
--- a/lib/chef/knife/key_list_base.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# 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.
-#
-
-class Chef
- class Knife
- # Extendable module that class_eval's common options into UserKeyList and ClientKeyList
- #
- # @author Tyler Cloke
- module KeyListBase
- 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."
-
- option :only_expired,
- 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."
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/key_show.rb b/lib/chef/knife/key_show.rb
deleted file mode 100644
index 8b3d980004..0000000000
--- a/lib/chef/knife/key_show.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# 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_relative "../key"
-require_relative "../json_compat"
-require_relative "../exceptions"
-
-class Chef
- class Knife
- # Service class for UserKeyShow and ClientKeyShow, used to show keys.
- # Implements common functionality of knife [user | org client] key show.
- #
- # @author Tyler Cloke
- #
- # @attr_accessor [Hash] cli input, see UserKeyShow and ClientKeyShow for what could populate it
- class KeyShow
-
- attr_accessor :config
-
- def initialize(name, actor, load_method, ui)
- @name = name
- @actor = actor
- @load_method = load_method
- @ui = ui
- end
-
- def display_output(key)
- @ui.output(@ui.format_for_display(key))
- end
-
- def run
- key = Chef::Key.send(@load_method, @actor, @name)
- key.public_key(key.public_key.strip)
- display_output(key)
- end
- end
- end
-end
diff --git a/lib/chef/knife/list.rb b/lib/chef/knife/list.rb
deleted file mode 100644
index 1cc398e01a..0000000000
--- a/lib/chef/knife/list.rb
+++ /dev/null
@@ -1,177 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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] (options)"
-
- category "path-based"
-
- deps do
- require_relative "../chef_fs/file_system"
- require "tty-screen"
- end
-
- option :recursive,
- 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."
-
- option :local,
- 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."
-
- option :one_column,
- short: "-1",
- boolean: true,
- description: "Show only one column of results."
-
- option :trailing_slashes,
- short: "-p",
- boolean: true,
- description: "Show trailing slashes after directories."
-
- attr_accessor :exit_code
-
- def run
- patterns = name_args.length == 0 ? [""] : name_args
-
- # Get the top-level matches
- all_results = parallelize(pattern_args_from(patterns)) do |pattern|
- pattern_results = Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).to_a
-
- if pattern_results.first && !pattern_results.first.exists? && pattern.exact_path
- ui.error "#{format_path(pattern_results.first)}: No such file or directory"
- self.exit_code = 1
- end
- pattern_results
- end.flatten(1).to_a
-
- # Process directories
- if !config[:bare_directories]
- dir_results = parallelize(all_results.select(&:dir?)) do |result|
- add_dir_result(result)
- end.flatten(1)
-
- else
- dir_results = []
- end
-
- # Process all other results
- results = all_results.select { |result| result.exists? && (!result.dir? || config[:bare_directories]) }.to_a
-
- # Flatten out directory results if necessary
- if config[:flat]
- 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(&:path)
- dir_results = dir_results.sort_by { |result| result[0].path }
-
- # Print!
- if results.length == 0 && dir_results.length == 1
- results = dir_results[0][1]
- dir_results = []
- end
-
- print_result_paths results
- printed_something = results.length > 0
- dir_results.each do |result, children|
- if printed_something
- output ""
- else
- printed_something = true
- end
- output "#{format_path(result)}:"
- print_results(children.map { |result| maybe_add_slash(result.display_name, result.dir?) }.sort, "")
- end
-
- exit exit_code if exit_code
- end
-
- def add_dir_result(result)
- begin
- children = result.children.sort_by(&:name)
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- return []
- end
-
- result = [ [ result, children ] ]
- if config[:recursive]
- child_dirs = children.select(&:dir?)
- result += parallelize(child_dirs) { |child| add_dir_result(child) }.flatten(1).to_a
- end
- result
- end
-
- def print_result_paths(results, indent = "")
- print_results(results.map { |result| maybe_add_slash(format_path(result), result.dir?) }, indent)
- end
-
- def print_results(results, indent)
- return if results.length == 0
-
- print_space = results.map(&:length).max + 2
- if config[:one_column] || !stdout.isatty
- columns = 0
- else
- columns = TTY::Screen.columns
- end
- current_line = ""
- results.each do |result|
- if current_line.length > 0 && current_line.length + print_space > columns
- output current_line.rstrip
- current_line = ""
- end
- if current_line.length == 0
- current_line << indent
- end
- current_line << result
- current_line << (" " * (print_space - result.length))
- end
- output current_line.rstrip if current_line.length > 0
- end
-
- def maybe_add_slash(path, is_dir)
- if config[:trailing_slashes] && is_dir
- "#{path}/"
- else
- path
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/node_bulk_delete.rb b/lib/chef/knife/node_bulk_delete.rb
deleted file mode 100644
index 874509b730..0000000000
--- a/lib/chef/knife/node_bulk_delete.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeBulkDelete < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- banner "knife node bulk delete REGEX (options)"
-
- def run
- if name_args.length < 1
- ui.fatal("You must supply a regular expression to match the results against")
- exit 42
- end
-
- nodes_to_delete = {}
- matcher = /#{name_args[0]}/
-
- all_nodes.each do |name, node|
- next unless name&.match?(matcher)
-
- nodes_to_delete[name] = node
- end
-
- if nodes_to_delete.empty?
- ui.msg "No nodes match the expression /#{name_args[0]}/"
- exit 0
- end
-
- ui.msg("The following nodes will be deleted:")
- ui.msg("")
- ui.msg(ui.list(nodes_to_delete.keys.sort, :columns_down))
- ui.msg("")
- ui.confirm("Are you sure you want to delete these nodes")
-
- nodes_to_delete.sort.each do |name, node|
- node.destroy
- ui.msg("Deleted node #{name}")
- end
- end
-
- def all_nodes
- node_uris_by_name = Chef::Node.list
-
- node_uris_by_name.keys.inject({}) do |nodes_by_name, name|
- nodes_by_name[name] = Chef::Node.new.tap { |n| n.name(name) }
- nodes_by_name
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_create.rb b/lib/chef/knife/node_create.rb
deleted file mode 100644
index c0db667b25..0000000000
--- a/lib/chef/knife/node_create.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeCreate < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- banner "knife node create NODE (options)"
-
- def run
- @node_name = @name_args[0]
-
- if @node_name.nil?
- show_usage
- ui.fatal("You must specify a node name")
- exit 1
- end
-
- node = Chef::Node.new
- node.name(@node_name)
- create_object(node, object_class: Chef::Node)
- end
- end
- end
-end
diff --git a/lib/chef/knife/node_delete.rb b/lib/chef/knife/node_delete.rb
deleted file mode 100644
index 7c0c6f0a21..0000000000
--- a/lib/chef/knife/node_delete.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeDelete < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- banner "knife node delete [NODE [NODE]] (options)"
-
- def run
- if @name_args.length == 0
- show_usage
- ui.fatal("You must specify at least one node name")
- exit 1
- end
-
- @name_args.each do |node_name|
- delete_object(Chef::Node, node_name)
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_edit.rb b/lib/chef/knife/node_edit.rb
deleted file mode 100644
index a2585391ea..0000000000
--- a/lib/chef/knife/node_edit.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeEdit < Knife
-
- deps do
- 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."
-
- def run
- if node_name.nil?
- show_usage
- ui.fatal("You must specify a node name")
- exit 1
- end
-
- updated_node = node_editor.edit_node
- if updated_values = node_editor.updated?
- ui.info "Saving updated #{updated_values.join(", ")} on node #{node.name}"
- updated_node.save
- else
- ui.info "Node not updated, skipping node save"
- end
- end
-
- def node_name
- @node_name ||= @name_args[0]
- end
-
- def node_editor
- @node_editor ||= Knife::NodeEditor.new(node, ui, config)
- end
-
- def node
- @node ||= Chef::Node.load(node_name)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_environment_set.rb b/lib/chef/knife/node_environment_set.rb
deleted file mode 100644
index 644b6138b6..0000000000
--- a/lib/chef/knife/node_environment_set.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Author:: Jimmy McCrory (<jimmy.mccrory@gmail.com>)
-# Copyright:: Copyright 2014-2016, Jimmy McCrory
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 NodeEnvironmentSet < Knife
-
- deps do
- require_relative "../node"
- end
-
- banner "knife node environment set NODE ENVIRONMENT"
-
- def run
- if @name_args.size < 2
- ui.fatal "You must specify a node name and an environment."
- show_usage
- exit 1
- else
- @node_name = @name_args[0]
- @environment = @name_args[1]
- end
-
- node = Chef::Node.load(@node_name)
-
- node.chef_environment = @environment
-
- node.save
-
- config[:environment] = @environment
- output(format_for_display(node))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_from_file.rb b/lib/chef/knife/node_from_file.rb
deleted file mode 100644
index 86d602ae7c..0000000000
--- a/lib/chef/knife/node_from_file.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeFromFile < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- require_relative "core/object_loader"
- end
-
- banner "knife node from file FILE (options)"
-
- def loader
- @loader ||= Knife::Core::ObjectLoader.new(Chef::Node, ui)
- end
-
- def run
- @name_args.each do |arg|
- updated = loader.load_from("nodes", arg)
-
- updated.save
-
- output(format_for_display(updated)) if config[:print_after]
-
- ui.info("Updated Node #{updated.name}")
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_list.rb b/lib/chef/knife/node_list.rb
deleted file mode 100644
index a8b57aedc5..0000000000
--- a/lib/chef/knife/node_list.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeList < Knife
-
- deps do
- 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."
-
- def run
- env = Chef::Config[:environment]
- output(format_list_for_display( env ? Chef::Node.list_by_environment(env) : Chef::Node.list ))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_policy_set.rb b/lib/chef/knife/node_policy_set.rb
deleted file mode 100644
index d34ebd9478..0000000000
--- a/lib/chef/knife/node_policy_set.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# 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
deleted file mode 100644
index 40476371eb..0000000000
--- a/lib/chef/knife/node_run_list_add.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeRunListAdd < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- 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."
-
- option :before,
- 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..].map do |entry|
- entry.split(",").map(&:strip)
- end.flatten
- else
- # Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map(&:strip)
- end
-
- if config[:after] && config[:before]
- ui.fatal("You cannot specify both --before and --after!")
- exit 1
- end
-
- if config[:after]
- add_to_run_list_after(node, entries, config[:after])
- elsif config[:before]
- add_to_run_list_before(node, entries, config[:before])
- else
- add_to_run_list_after(node, entries)
- end
-
- node.save
-
- config[:run_list] = true
-
- output(format_for_display(node))
- end
-
- private
-
- def add_to_run_list_after(node, entries, after = nil)
- if after
- nlist = []
- node.run_list.each do |entry|
- nlist << entry
- if entry == after
- entries.each { |e| nlist << e }
- end
- end
- node.run_list.reset!(nlist)
- else
- entries.each { |e| node.run_list << e }
- end
- end
-
- def add_to_run_list_before(node, entries, before)
- nlist = []
- node.run_list.each do |entry|
- if entry == before
- entries.each { |e| nlist << e }
- end
- nlist << entry
- end
- node.run_list.reset!(nlist)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_run_list_remove.rb b/lib/chef/knife/node_run_list_remove.rb
deleted file mode 100644
index 484e575475..0000000000
--- a/lib/chef/knife/node_run_list_remove.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 NodeRunListRemove < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- 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..].map do |entry|
- entry.split(",").map(&:strip)
- end.flatten
- else
- # Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map(&:strip)
- end
-
- # iterate over the list of things to remove,
- # warning if one of them was not found
- entries.each do |e|
- if node.run_list.find { |rli| e == rli.to_s }
- node.run_list.remove(e)
- else
- ui.warn "#{e} is not in the run list"
- unless /^(recipe|role)\[/.match?(e)
- ui.warn "(did you forget recipe[] or role[] around it?)"
- end
- end
- end
-
- node.save
-
- config[:run_list] = true
-
- output(format_for_display(node))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_run_list_set.rb b/lib/chef/knife/node_run_list_set.rb
deleted file mode 100644
index f356b39d95..0000000000
--- a/lib/chef/knife/node_run_list_set.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Author:: Mike Fiedler (<miketheman@gmail.com>)
-# Copyright:: Copyright 2013-2016, Mike Fiedler
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 NodeRunListSet < Knife
-
- deps do
- require_relative "../node"
- require_relative "../json_compat"
- end
-
- banner "knife node run_list set NODE ENTRIES (options)"
-
- def run
- if @name_args.size < 2
- ui.fatal "You must supply both a node name and a run list."
- show_usage
- exit 1
- elsif @name_args.size > 2
- # Check for nested lists and create a single plain one
- 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(&:strip)
- end
- node = Chef::Node.load(@name_args[0])
-
- set_run_list(node, entries)
-
- node.save
-
- config[:run_list] = true
-
- output(format_for_display(node))
- end
-
- # Clears out any existing run_list_items and sets them to the
- # specified entries
- def set_run_list(node, entries)
- node.run_list.run_list_items.clear
- entries.each { |e| node.run_list << e }
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/node_show.rb b/lib/chef/knife/node_show.rb
deleted file mode 100644
index 8ef06d8938..0000000000
--- a/lib/chef/knife/node_show.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-require_relative "core/node_presenter"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class NodeShow < Knife
-
- include Knife::Core::NodeFormattingOptions
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- 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."
-
- option :environment,
- short: "-E",
- long: "--environment",
- description: "Show only the #{ChefUtils::Dist::Infra::PRODUCT} environment."
-
- def run
- ui.use_presenter Knife::Core::NodePresenter
- @node_name = @name_args[0]
-
- if @node_name.nil?
- show_usage
- ui.fatal("You must specify a node name")
- exit 1
- end
-
- node = Chef::Node.load(@node_name)
- output(format_for_display(node))
- end
- end
- end
-end
diff --git a/lib/chef/knife/null.rb b/lib/chef/knife/null.rb
deleted file mode 100644
index 7221eee9f5..0000000000
--- a/lib/chef/knife/null.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class Chef
- class Knife
- class Null < Chef::Knife
- banner "knife null"
-
- # setting the category to deprecated keeps it out of help
- category "deprecated"
-
- def run; end
- end
- end
-end
diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb
deleted file mode 100644
index 5adb36ea70..0000000000
--- a/lib/chef/knife/raw.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 (options)"
-
- deps do
- 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."
-
- option :pretty,
- 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."
-
- option :proxy_auth,
- 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[: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
- use Chef::HTTP::CookieManager
- use Chef::HTTP::Decompressor
- use Chef::HTTP::Authenticator
- use Chef::HTTP::RemoteRequestID
- end
-
- def run
- if name_args.length == 0
- show_usage
- ui.fatal("You must provide the path you want to hit on the server")
- exit(1)
- elsif name_args.length > 1
- show_usage
- ui.fatal("You must specify only a single path")
- exit(1)
- end
-
- path = name_args[0]
- data = false
- if config[:input]
- data = IO.read(config[:input])
- end
- begin
- method = config[:method].to_sym
-
- headers = { "Content-Type" => "application/json" }
-
- if config[:proxy_auth]
- headers["x-ops-request-source"] = "web"
- end
-
- if config[:pretty]
- chef_rest = RawInputServerAPI.new
- result = chef_rest.request(method, name_args[0], headers, data)
- unless result.is_a?(String)
- result = Chef::JSONCompat.to_json_pretty(result)
- end
- else
- 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::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
- end
- end
-
- end # class Raw
- end
-end
diff --git a/lib/chef/knife/recipe_list.rb b/lib/chef/knife/recipe_list.rb
deleted file mode 100644
index 39e040a2f4..0000000000
--- a/lib/chef/knife/recipe_list.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# 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 "../knife"
-class Chef::Knife::RecipeList < Chef::Knife
-
- banner "knife recipe list [PATTERN]"
-
- def run
- recipes = rest.get("cookbooks/_recipes")
- if pattern = @name_args.first
- recipes = recipes.grep(Regexp.new(pattern))
- end
- output(recipes)
- end
-
-end
diff --git a/lib/chef/knife/rehash.rb b/lib/chef/knife/rehash.rb
deleted file mode 100644
index 69ee19229a..0000000000
--- a/lib/chef/knife/rehash.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# 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 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."
- ui.msg "However, you will need to update the cache by running `knife rehash` anytime you install a new knife plugin."
- else
- reload_plugins
- end
-
- 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
- # The subcommand_loader for this knife command should _always_ be the GemGlobLoader. The GemGlobLoader loads
- # plugins from disc and ensures the hash we write is always correct. By this point it should also already have
- # loaded plugins and `load_commands` shouldn't have an effect.
- Chef::Knife.subcommand_loader.load_commands
- end
- end
- end
-end
diff --git a/lib/chef/knife/role_bulk_delete.rb b/lib/chef/knife/role_bulk_delete.rb
deleted file mode 100644
index f57ac79619..0000000000
--- a/lib/chef/knife/role_bulk_delete.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleBulkDelete < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- banner "knife role bulk delete REGEX (options)"
-
- def run
- if @name_args.length < 1
- ui.error("You must supply a regular expression to match the results against")
- exit 1
- end
-
- all_roles = Chef::Role.list(true)
-
- matcher = /#{@name_args[0]}/
- roles_to_delete = {}
- all_roles.each do |name, role|
- next unless name&.match?(matcher)
-
- roles_to_delete[role.name] = role
- end
-
- if roles_to_delete.empty?
- ui.info "No roles match the expression /#{@name_args[0]}/"
- exit 0
- end
-
- ui.msg("The following roles will be deleted:")
- ui.msg("")
- ui.msg(ui.list(roles_to_delete.keys.sort, :columns_down))
- ui.msg("")
- ui.confirm("Are you sure you want to delete these roles")
-
- roles_to_delete.sort.each do |name, role|
- role.destroy
- ui.msg("Deleted role #{name}")
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/role_create.rb b/lib/chef/knife/role_create.rb
deleted file mode 100644
index 295445554d..0000000000
--- a/lib/chef/knife/role_create.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleCreate < Knife
-
- deps do
- 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."
-
- def run
- @role_name = @name_args[0]
-
- if @role_name.nil?
- show_usage
- ui.fatal("You must specify a role name")
- exit 1
- end
-
- role = Chef::Role.new
- role.name(@role_name)
- role.description(config[:description]) if config[:description]
- create_object(role, object_class: Chef::Role)
- end
- end
- end
-end
diff --git a/lib/chef/knife/role_delete.rb b/lib/chef/knife/role_delete.rb
deleted file mode 100644
index c46e265c5e..0000000000
--- a/lib/chef/knife/role_delete.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleDelete < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- banner "knife role delete ROLE (options)"
-
- def run
- @role_name = @name_args[0]
-
- if @role_name.nil?
- show_usage
- ui.fatal("You must specify a role name")
- exit 1
- end
-
- delete_object(Chef::Role, @role_name)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_edit.rb b/lib/chef/knife/role_edit.rb
deleted file mode 100644
index 1925336646..0000000000
--- a/lib/chef/knife/role_edit.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleEdit < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- banner "knife role edit ROLE (options)"
-
- def run
- @role_name = @name_args[0]
-
- if @role_name.nil?
- show_usage
- ui.fatal("You must specify a role name")
- exit 1
- end
-
- ui.edit_object(Chef::Role, @role_name)
- end
- end
- end
-end
diff --git a/lib/chef/knife/role_env_run_list_add.rb b/lib/chef/knife/role_env_run_list_add.rb
deleted file mode 100644
index b5753b46fc..0000000000
--- a/lib/chef/knife/role_env_run_list_add.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: William Albenzi (<walbenzi@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_relative "../knife"
-
-class Chef
- class Knife
- class RoleEnvRunListAdd < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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."
-
- def add_to_env_run_list(role, environment, entries, after = nil)
- if after
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- role.run_list_for(environment).each do |entry|
- nlist << entry
- if entry == after
- entries.each { |e| nlist << e }
- end
- end
- role.env_run_lists_add(environment => nlist)
- else
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- role.run_list_for(environment).each do |entry|
- nlist << entry
- end
- entries.each { |e| nlist << e }
- role.env_run_lists_add(environment => nlist)
- end
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = @name_args[1]
-
- if @name_args.size > 2
- # Check for nested lists and create a single plain one
- 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(&:strip)
- end
-
- add_to_env_run_list(role, environment, entries, config[:after])
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_env_run_list_clear.rb b/lib/chef/knife/role_env_run_list_clear.rb
deleted file mode 100644
index dda523e809..0000000000
--- a/lib/chef/knife/role_env_run_list_clear.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Author:: Mike Fiedler (<miketheman@gmail.com>)
-# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2013-2016, Mike Fiedler
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 RoleEnvRunListClear < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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)
- end
-
- def run
- if @name_args.size > 2
- ui.fatal "You must not supply an environment run list."
- show_usage
- exit 1
- end
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = @name_args[1]
-
- clear_env_run_list(role, environment)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_env_run_list_remove.rb b/lib/chef/knife/role_env_run_list_remove.rb
deleted file mode 100644
index 57363610ce..0000000000
--- a/lib/chef/knife/role_env_run_list_remove.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleEnvRunListRemove < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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]
- # nlist << entry
- # end
- end
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = @name_args[1]
- item_to_remove = @name_args[2]
-
- remove_from_env_run_list(role, environment, item_to_remove)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_env_run_list_replace.rb b/lib/chef/knife/role_env_run_list_replace.rb
deleted file mode 100644
index e76680661e..0000000000
--- a/lib/chef/knife/role_env_run_list_replace.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: William Albenzi (<walbenzi@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_relative "../knife"
-
-class Chef
- class Knife
- class RoleEnvRunListReplace < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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 = []
- role.run_list_for(environment).each do |entry|
- if entry == old_entry
- nlist << new_entry
- else
- nlist << entry
- end
- end
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = @name_args[1]
- old_entry = @name_args[2]
- new_entry = @name_args[3]
-
- replace_in_env_run_list(role, environment, old_entry, new_entry)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_env_run_list_set.rb b/lib/chef/knife/role_env_run_list_set.rb
deleted file mode 100644
index 0f1ce62a5d..0000000000
--- a/lib/chef/knife/role_env_run_list_set.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# Author:: Mike Fiedler (<miketheman@gmail.com>)
-# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2013-2016, Mike Fiedler
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 RoleEnvRunListSet < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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
- def set_env_run_list(role, environment, entries)
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- entries.each { |e| nlist << e }
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = @name_args[1]
- if @name_args.size < 2
- ui.fatal "You must supply both a role name and an environment run list."
- show_usage
- exit 1
- elsif @name_args.size > 2
- # Check for nested lists and create a single plain one
- 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(&:strip)
- end
-
- set_env_run_list(role, environment, entries )
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_from_file.rb b/lib/chef/knife/role_from_file.rb
deleted file mode 100644
index 16e38eeb63..0000000000
--- a/lib/chef/knife/role_from_file.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleFromFile < Knife
-
- deps do
- require_relative "../role"
- require_relative "core/object_loader"
- require_relative "../json_compat"
- end
-
- banner "knife role from file FILE [FILE..] (options)"
-
- def loader
- @loader ||= Knife::Core::ObjectLoader.new(Chef::Role, ui)
- end
-
- def run
- @name_args.each do |arg|
- updated = loader.load_from("roles", arg)
-
- updated.save
-
- output(format_for_display(updated)) if config[:print_after]
-
- ui.info("Updated Role #{updated.name}")
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_list.rb b/lib/chef/knife/role_list.rb
deleted file mode 100644
index d6aad053c1..0000000000
--- a/lib/chef/knife/role_list.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleList < Knife
-
- deps do
- 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."
-
- def run
- output(format_list_for_display(Chef::Role.list))
- end
- end
- end
-end
diff --git a/lib/chef/knife/role_run_list_add.rb b/lib/chef/knife/role_run_list_add.rb
deleted file mode 100644
index 76633ff5f6..0000000000
--- a/lib/chef/knife/role_run_list_add.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: William Albenzi (<walbenzi@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_relative "../knife"
-
-class Chef
- class Knife
- class RoleRunListAdd < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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."
-
- def add_to_env_run_list(role, environment, entries, after = nil)
- if after
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- role.run_list_for(environment).each do |entry|
- nlist << entry
- if entry == after
- entries.each { |e| nlist << e }
- end
- end
- role.env_run_lists_add(environment => nlist)
- else
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- role.run_list_for(environment).each do |entry|
- nlist << entry
- end
- entries.each { |e| nlist << e }
- role.env_run_lists_add(environment => nlist)
- end
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = "_default"
-
- if @name_args.size > 1
- # Check for nested lists and create a single plain one
- 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(&:strip)
- end
-
- add_to_env_run_list(role, environment, entries, config[:after])
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_run_list_clear.rb b/lib/chef/knife/role_run_list_clear.rb
deleted file mode 100644
index b7106233f0..0000000000
--- a/lib/chef/knife/role_run_list_clear.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Author:: Mike Fiedler (<miketheman@gmail.com>)
-# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2013-2016, Mike Fiedler
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 RoleRunListClear < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- banner "knife role run_list clear [ROLE] (options)"
- def clear_env_run_list(role, environment)
- nlist = []
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- if @name_args.size > 2
- ui.fatal "You must not supply an environment run list."
- show_usage
- exit 1
- end
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = "_default"
-
- clear_env_run_list(role, environment)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_run_list_remove.rb b/lib/chef/knife/role_run_list_remove.rb
deleted file mode 100644
index 884f3bc28d..0000000000
--- a/lib/chef/knife/role_run_list_remove.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleRunListRemove < Knife
-
- deps do
- require_relative "../role"
- end
-
- 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]
- # nlist << entry
- # end
- end
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = "_default"
- item_to_remove = @name_args[1]
-
- remove_from_env_run_list(role, environment, item_to_remove)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_run_list_replace.rb b/lib/chef/knife/role_run_list_replace.rb
deleted file mode 100644
index 16f789fbef..0000000000
--- a/lib/chef/knife/role_run_list_replace.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: William Albenzi (<walbenzi@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_relative "../knife"
-
-class Chef
- class Knife
- class RoleRunListReplace < Knife
-
- deps do
- require_relative "../role"
- require_relative "../json_compat"
- end
-
- 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 = []
- role.run_list_for(environment).each do |entry|
- if entry == old_entry
- nlist << new_entry
- else
- nlist << entry
- end
- end
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = "_default"
- old_entry = @name_args[1]
- new_entry = @name_args[2]
-
- replace_in_env_run_list(role, environment, old_entry, new_entry)
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_run_list_set.rb b/lib/chef/knife/role_run_list_set.rb
deleted file mode 100644
index ad1a5e2923..0000000000
--- a/lib/chef/knife/role_run_list_set.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Author:: Mike Fiedler (<miketheman@gmail.com>)
-# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2013-2016, Mike Fiedler
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 RoleRunListSet < Knife
-
- deps do
- require_relative "../role"
- end
-
- banner "knife role run_list set [ROLE] [ENTRIES] (options)"
-
- # Clears out any existing env_run_list_items and sets them to the
- # specified entries
- def set_env_run_list(role, environment, entries)
- nlist = []
- unless role.env_run_lists.key?(environment)
- role.env_run_lists_add(environment => nlist)
- end
- entries.each { |e| nlist << e }
- role.env_run_lists_add(environment => nlist)
- end
-
- def run
- role = Chef::Role.load(@name_args[0])
- role.name(@name_args[0])
- environment = "_default"
- if @name_args.size < 1
- ui.fatal "You must supply both a role name and an environment run list."
- show_usage
- exit 1
- elsif @name_args.size > 1
- # Check for nested lists and create a single plain one
- 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(&:strip)
- end
-
- set_env_run_list(role, environment, entries )
- role.save
- config[:env_run_list] = true
- output(format_for_display(role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/role_show.rb b/lib/chef/knife/role_show.rb
deleted file mode 100644
index ee90352e50..0000000000
--- a/lib/chef/knife/role_show.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 RoleShow < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require_relative "../role"
- end
-
- banner "knife role show ROLE (options)"
-
- def run
- @role_name = @name_args[0]
-
- if @role_name.nil?
- show_usage
- ui.fatal("You must specify a role name.")
- exit 1
- end
-
- role = Chef::Role.load(@role_name)
- output(format_for_display(config[:environment] ? role.environment(config[:environment]) : role))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
deleted file mode 100644
index 2feb8e6729..0000000000
--- a/lib/chef/knife/search.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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"
-require_relative "core/node_presenter"
-
-class Chef
- class Knife
- class Search < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require_relative "../node"
- require_relative "../environment"
- require_relative "../api_client"
- require_relative "../search/query"
- end
-
- include Knife::Core::NodeFormattingOptions
-
- banner "knife search INDEX QUERY (options)"
-
- option :start,
- 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 }
-
- option :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."
-
- option :query,
- 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\"."
-
- def run
- read_cli_args
-
- if @type == "node"
- ui.use_presenter Knife::Core::NodePresenter
- end
-
- q = Chef::Search::Query.new
-
- result_items = []
- result_count = 0
-
- 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, @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
- formatted_item = format_for_display(item)
- end
- result_items << formatted_item
- result_count += 1
- end
- rescue Net::HTTPClientException => e
- msg = Chef::JSONCompat.from_json(e.response.body)["error"].first
- ui.error("knife search failed: #{msg}")
- exit 99
- end
-
- if ui.interchange?
- output({ results: result_count, rows: result_items })
- else
- ui.log "#{result_count} items found"
- ui.log("\n")
- result_items.each do |item|
- output(item)
- unless config[:id_only]
- ui.msg("\n")
- 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
- if config[:query]
- if @name_args[1]
- ui.error "Please specify query as an argument or an option via -q, not both"
- ui.msg opt_parser
- exit 1
- end
- @type = name_args[0]
- @query = config[:query]
- else
- case name_args.size
- when 0
- ui.error "No query specified"
- ui.msg opt_parser
- exit 1
- when 1
- @type = "node"
- @query = name_args[0]
- when 2
- @type = name_args[0]
- @query = name_args[1]
- end
- 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:
- # -f "return_var1=path.to.attribute, return_var2=shorter.path"
- #
- # a more concrete example might be:
- # -f "env=chef_environment, ruby_platform=languages.ruby.platform"
- #
- # The end result is a hash where the key is a symbol in the hash (the return variable)
- # 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 = {}
- 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
- final_filter
- end
-
- def create_result_filter_from_attributes(filter_array)
- 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" ]
- final_filter
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/serve.rb b/lib/chef/knife/serve.rb
deleted file mode 100644
index d79e05aa85..0000000000
--- a/lib/chef/knife/serve.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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
- class Serve < Knife
-
- 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."
-
- option :chef_repo_path,
- 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 #{ChefUtils::Dist::Zero::PRODUCT} listens. Default is 127.0.0.1."
-
- def configure_chef
- super
- Chef::Config.local_mode = true
- Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode]
-
- # --chef-repo-path forcibly overrides all other paths
- if config[:chef_repo_path]
- Chef::Config.chef_repo_path = config[:chef_repo_path]
- %w{acl client cookbook container data_bag environment group node role user}.each do |variable_name|
- Chef::Config.delete("#{variable_name}_path".to_sym)
- end
- end
- end
-
- def run
- server = Chef::LocalMode.chef_zero_server
- begin
- output "Serving files from:\n#{Chef::LocalMode.chef_fs.fs_description}"
- server.stop
- server.start(stdout) # to print header
- ensure
- server.stop
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/show.rb b/lib/chef/knife/show.rb
deleted file mode 100644
index 0e5ab9d0fe..0000000000
--- a/lib/chef/knife/show.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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] (options)"
-
- category "path-based"
-
- deps do
- 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."
-
- def run
- # Get the matches (recursively)
- error = false
- entry_values = parallelize(pattern_args) do |pattern|
- parallelize(Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern)) do |entry|
- if entry.dir?
- ui.error "#{format_path(entry)}: is a directory" if pattern.exact_path
- error = true
- nil
- else
- begin
- [entry, entry.read]
- rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
- ui.error "#{format_path(e.entry)}: #{e.reason}."
- error = true
- nil
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- error = true
- nil
- end
- end
- end
- end.flatten(1)
- entry_values.each do |entry, value|
- if entry
- output "#{format_path(entry)}:"
- output(format_for_display(value))
- end
- end
- if error
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
deleted file mode 100644
index a586bf37c2..0000000000
--- a/lib/chef/knife/ssh.rb
+++ /dev/null
@@ -1,643 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 Ssh < Knife
-
- deps do
- require_relative "../mixin/shell_out"
- require "net/ssh" unless defined?(Net::SSH)
- require "net/ssh/multi"
- require "readline"
- require_relative "../exceptions"
- require_relative "../search/query"
- require_relative "../util/path_helper"
-
- 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 :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
-
- 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,
- 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
-
- option :ssh_port,
- 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| key.strip.to_i },
- default: 120
-
- option :ssh_gateway,
- short: "-G GATEWAY",
- long: "--ssh-gateway GATEWAY",
- description: "The ssh gateway.",
- proc: Proc.new { |key| key.strip }
-
- option :ssh_gateway_identity,
- long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
- description: "The SSH identity file used for gateway authentication."
-
- option :forward_agent,
- short: "-A",
- long: "--forward-agent",
- description: "Enable SSH agent forwarding.",
- boolean: true
-
- option :ssh_identity_file,
- 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
-
- option :on_error,
- 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
-
- def session
- ssh_error_handler = Proc.new do |server|
- if config[:on_error]
- # Net::SSH::Multi magic to force exception to be re-raised.
- throw :go, :raise
- else
- ui.warn "Failed to connect to #{server.host} -- #{$!.class.name}: #{$!.message}"
- $!.backtrace.each { |l| Chef::Log.debug(l) }
- end
- end
-
- @session ||= Net::SSH::Multi.start(concurrent_connections: config[:concurrency], on_error: ssh_error_handler)
- end
-
- def configure_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, gateway: true)
- user = gw_opts.delete(:user)
-
- begin
- # Try to connect with a key.
- session.via(gw_host, user, gw_opts)
- rescue Net::SSH::AuthenticationFailed
- prompt = "Enter the password for #{user}@#{gw_host}: "
- gw_opts[:password] = prompt_for_password(prompt)
- # Try again with a password.
- session.via(gw_host, user, gw_opts)
- end
- end
- end
-
- def configure_session
- list = config[:manual] ? @name_args[0].split(" ") : search_nodes
- if list.length == 0
- if @search_count == 0
- ui.fatal("No nodes returned from search")
- else
- 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_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) 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
- Chef::Log.debug(sprintf(msg, "fqdn", item["fqdn"]))
- item["fqdn"]
- end
- end
-
- def search_nodes
- list = []
- query = Chef::Search::Query.new
- 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 = get_ssh_attribute(item)
- next if host.nil?
-
- prefix = get_prefix_attribute(item)
- ssh_port = item.dig("cloud", "public_ssh_port")
- srv = [host, ssh_port, prefix]
- list.push(srv)
- end
-
- list
- end
-
- # Net::SSH session options hash for global options. These should be
- # options that will apply to the gateway connection in addition to the
- # main one.
- #
- # @since 12.5.0
- # @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, gateway: false)
- ssh_config = Net::SSH.configuration_for(host, true)
- {}.tap do |opts|
- opts[:user] = user || config[:ssh_user] || ssh_config[:user]
- 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
- # Don't set the keys to nil if we don't have them.
- forward_agent = config[:forward_agent] || ssh_config[:forward_agent]
- opts[:forward_agent] = forward_agent unless forward_agent.nil?
- port ||= ssh_config[:port]
- opts[:port] = port unless port.nil?
- opts[:logger] = Chef::Log.with_child(subsystem: "net/ssh") if Chef::Log.level == :trace
- unless config[:host_key_verify]
- opts[:verify_host_key] = false
- opts[:user_known_hosts_file] = "/dev/null"
- end
- if ssh_config[:keepalive]
- opts[:keepalive] = true
- opts[:keepalive_interval] = ssh_config[:keepalive_interval]
- end
- # 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, prefix = item
- prefix = host unless prefix
- Chef::Log.debug("Adding #{host}")
- session_opts = session_options(host, ssh_port, gateway: false)
- # Handle port overrides for the main connection.
- session_opts[:port] = config[:ssh_port] if config[:ssh_port]
- # Handle connection 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 = prefix.length if prefix.length > @longest
- end
-
- session
- end
-
- def fixup_sudo(command)
- command.sub(/^sudo/, "sudo -p 'knife sudo password: '")
- end
-
- def print_data(host, data)
- @buffers ||= {}
- if leftover = @buffers[host]
- @buffers[host] = nil
- print_data(host, leftover + data)
- else
- if newline_index = data.index("\n")
- line = data.slice!(0...newline_index)
- data.slice!(0)
- print_line(host, line)
- print_data(host, data)
- else
- @buffers[host] = data
- end
- end
- end
-
- def print_line(host, data)
- padding = @longest - host.length
- str = ui.color(host, :cyan) + (" " * (padding + 1)) + data
- ui.msg(str)
- end
-
- def ssh_command(command, subsession = nil)
- exit_status = 0
- subsession ||= session
- command = fixup_sudo(command)
- command.force_encoding("binary") if command.respond_to?(:force_encoding)
- 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|
- stderr += data
- end
-
- ch.on_request "exit-status" do |ichannel, data|
- exit_status = [exit_status, data.read_long].max
- end
- end
- end
- end
- session.loop
- exit_status
- end
-
- def get_password
- @password ||= prompt_for_password
- end
-
- def prompt_for_password(prompt = "Enter your password: ")
- ui.ask(prompt, echo: false)
- end
-
- # Present the prompt and read a single line from the console. It also
- # detects ^D and returns "exit" in that case. Adds the input to the
- # history, unless the input is empty. Loops repeatedly until a non-empty
- # line is input.
- def read_line
- loop do
- command = reader.readline("#{ui.color("knife-ssh>", :bold)} ", true)
-
- if command.nil?
- command = "exit"
- puts(command)
- else
- command.strip!
- end
-
- unless command.empty?
- return command
- end
- end
- end
-
- def reader
- Readline
- end
-
- def interactive
- puts "Connected to #{ui.list(session.servers_for.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
- puts
- puts "To run a command on a list of servers, do:"
- puts " on SERVER1 SERVER2 SERVER3; COMMAND"
- puts " Example: on latte foamy; echo foobar"
- puts
- puts "To exit interactive mode, use 'quit!'"
- puts
- loop do
- command = read_line
- case command
- when "quit!"
- puts "Bye!"
- break
- when /^on (.+?); (.+)$/
- raw_list = $1.split(" ")
- server_list = []
- session.servers.each do |session_server|
- server_list << session_server if raw_list.include?(session_server.host)
- end
- command = $2
- ssh_command(command, session.on(*server_list))
- else
- ssh_command(command)
- end
- end
- end
-
- def screen
- tf = Tempfile.new("knife-ssh-screen")
- Chef::Util::PathHelper.home(".screenrc") do |screenrc_path|
- if File.exist? screenrc_path
- tf.puts("source #{screenrc_path}")
- end
- end
- tf.puts("caption always '%-Lw%{= BW}%50>%n%f* %t%{-}%+Lw%<'")
- tf.puts("hardstatus alwayslastline 'knife ssh #{@name_args[0]}'")
- window = 0
- session.servers_for.each do |server|
- tf.print("screen -t \"#{server.host}\" #{window} ssh ")
- tf.print("-i #{config[:ssh_identity_file]} ") if config[:ssh_identity_file]
- server.user ? tf.puts("#{server.user}@#{server.host}") : tf.puts(server.host)
- window += 1
- end
- tf.close
- exec("screen -c #{tf.path}")
- end
-
- def tmux
- ssh_dest = lambda do |server|
- identity = "-i #{config[:ssh_identity_file]} " if config[:ssh_identity_file]
- prefix = server.user ? "#{server.user}@" : ""
- "'ssh #{identity}#{prefix}#{server.host}'"
- end
-
- new_window_cmds = lambda do
- if session.servers_for.size > 1
- [""] + session.servers_for[1..].map do |server|
- if config[:tmux_split]
- "split-window #{ssh_dest.call(server)}; tmux select-layout tiled"
- else
- "new-window -a -n '#{server.host}' #{ssh_dest.call(server)}"
- end
- end
- else
- []
- end.join(" \\; ")
- end
-
- tmux_name = "'knife ssh #{@name_args[0].tr(":.", "=-")}'"
- begin
- server = session.servers_for.first
- cmd = ["tmux new-session -d -s #{tmux_name}",
- "-n '#{server.host}'", ssh_dest.call(server),
- new_window_cmds.call].join(" ")
- shell_out!(cmd)
- exec("tmux attach-session -t #{tmux_name}")
- rescue Chef::Exceptions::Exec
- end
- end
-
- def macterm
- begin
- 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)
- 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)
- 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)
- end
- end
-
- def cssh
- cssh_cmd = nil
- %w{csshX cssh}.each do |cmd|
-
- # 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
-
- # pass in the consolidated identity file option to cssh(X)
- if config[:ssh_identity_file]
- cssh_cmd << " --ssh_args '-i #{File.expand_path(config[:ssh_identity_file])}'"
- end
-
- session.servers_for.each do |server|
- cssh_cmd << " #{server.user ? "#{server.user}@#{server.host}" : server.host}"
- end
- Chef::Log.debug("Starting cssh session with command: #{cssh_cmd}")
- exec(cssh_cmd)
- end
-
- def get_stripped_unfrozen_value(value)
- return nil unless value
-
- value.strip
- end
-
- def configure_user
- config[:ssh_user] = get_stripped_unfrozen_value(config[:ssh_user] ||
- Chef::Config[:knife][:ssh_user])
- end
-
- def configure_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
- # 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[:ssh_identity_file] = get_stripped_unfrozen_value(config[:ssh_identity_file])
- end
-
- 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
- @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
-
- exit_status =
- case @name_args[1]
- when "interactive"
- interactive
- when "screen"
- screen
- when "tmux"
- tmux
- when "macterm"
- macterm
- when "cssh"
- cssh
- else
- ssh_command(@name_args[1..].join(" "))
- end
-
- session.close
- if exit_status && exit_status != 0
- exit exit_status
- else
- exit_status
- end
- end
-
- private :search_nodes
-
- end
- end
-end
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
deleted file mode 100644
index 0cc4141d42..0000000000
--- a/lib/chef/knife/ssl_check.rb
+++ /dev/null
@@ -1,284 +0,0 @@
-#
-# 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 "../knife"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class SslCheck < Chef::Knife
-
- deps do
- 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
-
- banner "knife ssl check [URL] (options)"
-
- def initialize(*args)
- @host = nil
- @verify_peer_socket = nil
- @ssl_policy = HTTP::DefaultSSLPolicy
- super
- end
-
- def uri
- @uri ||= begin
- Chef::Log.trace("Checking SSL cert on #{given_uri}")
- URI.parse(given_uri)
- end
- end
-
- def given_uri
- (name_args[0] || Chef::Config.chef_server_url)
- end
-
- def host
- uri.host
- end
-
- def port
- uri.port
- end
-
- def validate_uri
- unless host && port
- invalid_uri!
- end
- rescue URI::Error
- invalid_uri!
- end
-
- def invalid_uri!
- ui.error("Given URI: `#{given_uri}' is invalid")
- show_usage
- exit 1
- end
-
- def verify_peer_socket
- @verify_peer_socket ||= begin
- tcp_connection = proxified_socket(host, port)
- ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context)
- ssl_client.hostname = host
- ssl_client
- end
- end
-
- def verify_peer_ssl_context
- @verify_peer_ssl_context ||= begin
- verify_peer_context = OpenSSL::SSL::SSLContext.new
- @ssl_policy.apply_to(verify_peer_context)
- verify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
- verify_peer_context
- end
- end
-
- def noverify_socket
- @noverify_socket ||= begin
- tcp_connection = proxified_socket(host, port)
- OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context)
- end
- end
-
- def noverify_peer_ssl_context
- @noverify_peer_ssl_context ||= begin
- noverify_peer_context = OpenSSL::SSL::SSLContext.new
- @ssl_policy.apply_to(noverify_peer_context)
- noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
- noverify_peer_context
- end
- end
-
- def verify_X509
- cert_debug_msg = ""
- trusted_certificates.each do |cert_name|
- message = check_X509_certificate(cert_name)
- unless message.nil?
- cert_debug_msg << File.expand_path(cert_name) + ": " + message + "\n"
- end
- end
-
- unless cert_debug_msg.empty?
- debug_invalid_X509(cert_debug_msg)
- end
-
- true # Maybe the bad certs won't hurt...
- end
-
- def verify_cert
- ui.msg("Connecting to host #{host}:#{port}")
- verify_peer_socket.connect
- true
- rescue OpenSSL::SSL::SSLError => e
- ui.error "The SSL certificate of #{host} could not be verified"
- Chef::Log.trace e.message
- debug_invalid_cert
- false
- end
-
- def verify_cert_host
- verify_peer_socket.post_connection_check(host)
- 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.trace(e)
- debug_invalid_host
- false
- end
-
- def debug_invalid_X509(cert_debug_msg)
- ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n")
- 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:
-
- #{cert_debug_msg}
-
- #{ui.color("TO FIX THESE WARNINGS:", :bold)}
-
- 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:
-
- /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.
-
- BAD_CERTS
- # @TODO: ^ needs URL once documentation is posted.
- end
-
- def debug_invalid_cert
- noverify_socket.connect
- issuer_info = noverify_socket.peer_cert.issuer
- ui.msg("Certificate issuer data: #{issuer_info}")
-
- ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n")
- debug_ssl_settings
- debug_chef_ssl_config
-
- ui.err(<<~ADVICE)
-
- #{ui.color("TO FIX THIS ERROR:", :bold)}
-
- 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:
-
- /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.
-
- ADVICE
- end
-
- def debug_invalid_host
- noverify_socket.connect
- subject = noverify_socket.peer_cert.subject
- cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" }
- cn = cn_field_tuple[1]
-
- ui.error("You are attempting to connect to: '#{host}'")
- ui.error("The server's certificate belongs to '#{cn}'")
- ui.err(<<~ADVICE)
-
- #{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.
-
- 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
- ui.err "OpenSSL Configuration:"
- ui.err "* Version: #{OpenSSL::OPENSSL_VERSION}"
- ui.err "* Certificate file: #{OpenSSL::X509::DEFAULT_CERT_FILE}"
- ui.err "* Certificate directory: #{OpenSSL::X509::DEFAULT_CERT_DIR}"
- end
-
- def debug_chef_ssl_config
- 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}"
- end
-
- def configuration
- Chef::Config
- end
-
- def run
- validate_uri
-
- if verify_X509 && verify_cert && verify_cert_host
- ui.msg "Successfully verified certificates from `#{host}'"
- else
- exit 1
- end
- end
-
- private
-
- def trusted_certificates
- if configuration.trusted_certs_dir && Dir.exist?(configuration.trusted_certs_dir)
- glob_dir = ChefConfig::PathHelper.escape_glob_dir(configuration.trusted_certs_dir)
- Dir.glob(File.join(glob_dir, "*.{crt,pem}"))
- else
- []
- end
- end
-
- def check_X509_certificate(cert_file)
- store = OpenSSL::X509::Store.new
- cert = OpenSSL::X509::Certificate.new(IO.read(File.expand_path(cert_file)))
- begin
- store.add_cert(cert)
- # test if the store can verify the cert we just added
- unless store.verify(cert) # true if verified, false if not
- return store.error_string
- end
- rescue OpenSSL::X509::StoreError => e
- return e.message
- end
- nil
- end
- end
- end
-end
diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb
deleted file mode 100644
index cfbbc823b2..0000000000
--- a/lib/chef/knife/ssl_fetch.rb
+++ /dev/null
@@ -1,161 +0,0 @@
-#
-# 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 "../knife"
-
-class Chef
- class Knife
- class SslFetch < Chef::Knife
-
- deps do
- 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
-
- banner "knife ssl fetch [URL] (options)"
-
- def initialize(*args)
- super
- @uri = nil
- end
-
- def uri
- @uri ||= begin
- Chef::Log.trace("Checking SSL cert on #{given_uri}")
- URI.parse(given_uri)
- end
- end
-
- def given_uri
- (name_args[0] || Chef::Config.chef_server_url)
- end
-
- def host
- uri.host
- end
-
- def port
- uri.port
- end
-
- def validate_uri
- unless host && port
- invalid_uri!
- end
- rescue URI::Error
- invalid_uri!
- end
-
- def invalid_uri!
- ui.error("Given URI: `#{given_uri}' is invalid")
- show_usage
- exit 1
- end
-
- def remote_cert_chain
- tcp_connection = proxified_socket(host, port)
- shady_ssl_connection = OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context)
- shady_ssl_connection.connect
- shady_ssl_connection.peer_cert_chain
- end
-
- def noverify_peer_ssl_context
- @noverify_peer_ssl_context ||= begin
- noverify_peer_context = OpenSSL::SSL::SSLContext.new
- noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
- noverify_peer_context
- end
- end
-
- def cn_of(certificate)
- subject = certificate.subject
- 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 hyphen
- # characters are converted to underscores.
- # 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.
- # https://tools.ietf.org/html/rfc6125#section-6.4.2
- def normalize_cn(cn)
- cn.gsub("*", "wildcard").gsub(/[^[:alnum:]\-]/, "_")
- end
-
- def configuration
- Chef::Config
- end
-
- def trusted_certs_dir
- configuration.trusted_certs_dir
- end
-
- def write_cert(cert)
- FileUtils.mkdir_p(trusted_certs_dir)
- cn = cn_of(cert)
- 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}).
-
- Knife has no means to verify these are the correct certificates. You should
- verify the authenticity of these certificates after downloading.
-
- TRUST_TRUST
- remote_cert_chain.each do |cert|
- write_cert(cert)
- end
- rescue OpenSSL::SSL::SSLError => e
- # 'unknown protocol' usually means you tried to connect to a non-ssl
- # service. We handle that specially here, any other error we let bubble
- # up (probably a bug of some sort).
- raise unless e.message.include?("unknown protocol")
-
- ui.error("The service at the given URI (#{uri}) does not accept SSL connections")
-
- if uri.scheme == "http"
- https_uri = uri.to_s.sub(/^http/, "https")
- ui.error("Perhaps you meant to connect to '#{https_uri}'?")
- end
- exit 1
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/status.rb b/lib/chef/knife/status.rb
deleted file mode 100644
index ea5dffdf6c..0000000000
--- a/lib/chef/knife/status.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-#
-# Author:: Ian Meyer (<ianmmeyer@gmail.com>)
-# Copyright:: Copyright 2010-2020, Ian Meyer
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 "core/status_presenter"
-require_relative "core/node_presenter"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class Status < Knife
- include Knife::Core::NodeFormattingOptions
-
- deps do
- require_relative "../search/query"
- end
-
- banner "knife status QUERY (options)"
-
- option :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_by_mins,
- 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?
- @query << term
- end
-
- def run
- ui.use_presenter Knife::Core::StatusPresenter
-
- if config[:long_output]
- opts = {}
- else
- opts = { filter_result:
- { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"],
- cloud: ["cloud"], run_list: ["run_list"], platform: ["platform"],
- platform_version: ["platform_version"], chef_environment: ["chef_environment"] } }
- end
-
- @query ||= ""
- append_to_query(@name_args[0]) if @name_args[0]
- append_to_query("chef_environment:#{config[:environment]}") if config[:environment]
-
- if config[:hide_by_mins]
- hidemins = 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}]"
- end
-
- @query = @query.empty? ? "*:*" : @query
-
- all_nodes = []
- q = Chef::Search::Query.new
- Chef::Log.info("Sending query: #{@query}")
- q.search(:node, @query, opts) do |node|
- all_nodes << node
- end
-
- output(all_nodes.sort do |n1, n2|
- if config[:sort_reverse] || config[:sort_status_reverse]
- (n2["ohai_time"] || 0) <=> (n1["ohai_time"] || 0)
- else
- (n1["ohai_time"] || 0) <=> (n2["ohai_time"] || 0)
- end
- end)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/supermarket_download.rb b/lib/chef/knife/supermarket_download.rb
deleted file mode 100644
index 5acd733b78..0000000000
--- a/lib/chef/knife/supermarket_download.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index a3d3aa7a5d..0000000000
--- a/lib/chef/knife/supermarket_install.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index 7dca8d031b..0000000000
--- a/lib/chef/knife/supermarket_list.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index 57befaed35..0000000000
--- a/lib/chef/knife/supermarket_search.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index 49b3474566..0000000000
--- a/lib/chef/knife/supermarket_share.rb
+++ /dev/null
@@ -1,166 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index 7237cf0bc7..0000000000
--- a/lib/chef/knife/supermarket_show.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 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
deleted file mode 100644
index 686d95f47a..0000000000
--- a/lib/chef/knife/supermarket_unshare.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# Author:: Christopher Webber (<cwebber@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 SupermarketUnshare < Knife
-
- 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
deleted file mode 100644
index 2f0d302e74..0000000000
--- a/lib/chef/knife/tag_create.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Author:: Ryan Davis (<ryand-ruby@zenspider.com>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2011-2016, Ryan Davis and Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 TagCreate < Knife
-
- deps do
- require_relative "../node"
- end
-
- banner "knife tag create NODE TAG ..."
-
- def run
- name = @name_args[0]
- tags = @name_args[1..]
-
- if name.nil? || tags.nil? || tags.empty?
- show_usage
- ui.fatal("You must specify a node name and at least one tag.")
- exit 1
- end
-
- node = Chef::Node.load name
- tags.each do |tag|
- (node.tags << tag).uniq!
- end
- node.save
- ui.info("Created tags #{tags.join(", ")} for node #{name}.")
- end
- end
- end
-end
diff --git a/lib/chef/knife/tag_delete.rb b/lib/chef/knife/tag_delete.rb
deleted file mode 100644
index 85fa6a9e27..0000000000
--- a/lib/chef/knife/tag_delete.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Author:: Ryan Davis (<ryand-ruby@zenspider.com>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2011-2016, Ryan Davis and Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 TagDelete < Knife
-
- deps do
- require_relative "../node"
- end
-
- banner "knife tag delete NODE TAG ..."
-
- def run
- name = @name_args[0]
- tags = @name_args[1..]
-
- if name.nil? || tags.nil? || tags.empty?
- show_usage
- ui.fatal("You must specify a node name and at least one tag.")
- exit 1
- end
-
- node = Chef::Node.load name
- deleted_tags = []
- tags.each do |tag|
- unless node.tags.delete(tag).nil?
- deleted_tags << tag
- end
- end
- node.save
- message = if deleted_tags.empty?
- "Nothing has changed. The tags requested to be deleted do not exist."
- else
- "Deleted tags #{deleted_tags.join(", ")} for node #{name}."
- end
- ui.info(message)
- end
- end
- end
-end
diff --git a/lib/chef/knife/tag_list.rb b/lib/chef/knife/tag_list.rb
deleted file mode 100644
index 8b91034609..0000000000
--- a/lib/chef/knife/tag_list.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Author:: Ryan Davis (<ryand-ruby@zenspider.com>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2011-2016, Ryan Davis and Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 TagList < Knife
-
- deps do
- require_relative "../node"
- end
-
- banner "knife tag list NODE"
-
- def run
- name = @name_args[0]
-
- if name.nil?
- show_usage
- ui.fatal("You must specify a node name.")
- exit 1
- end
-
- node = Chef::Node.load(name)
- output(node.tags)
- end
- end
- end
-end
diff --git a/lib/chef/knife/upload.rb b/lib/chef/knife/upload.rb
deleted file mode 100644
index 190549d86a..0000000000
--- a/lib/chef/knife/upload.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 (options)"
-
- category "path-based"
-
- deps do
- require_relative "../chef_fs/command_line"
- end
-
- option :recurse,
- 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."
-
- 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."
-
- option :freeze,
- 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."
-
- 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."
-
- def run
- if name_args.length == 0
- show_usage
- ui.fatal("You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"")
- exit 1
- end
-
- error = false
- pattern_args.each do |pattern|
- if Chef::ChefFS::FileSystem.copy_to(pattern, local_fs, chef_fs, config[:recurse] ? nil : 1, config, ui, proc { |entry| format_path(entry) })
- error = true
- end
- end
- if error
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb
deleted file mode 100644
index 6d68f3ebbb..0000000000
--- a/lib/chef/knife/user_create.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-#
-# Author:: Steven Danna (<steve@chef.io>)
-# 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_relative "../knife"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Knife
- class UserCreate < Knife
-
- attr_accessor :user_field
-
- deps do
- 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."
-
- 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)."
-
- option :prevent_keygen,
- 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)"
-
- def user
- @user_field ||= Chef::UserV1.new
- end
-
- def create_user_from_hash(hash)
- Chef::UserV1.from_hash(hash).create
- end
-
- def run
- 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[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[4], "email")
- user.email @name_args[4]
-
- 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[: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
-
- 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
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb
deleted file mode 100644
index 87c1f734bb..0000000000
--- a/lib/chef/knife/user_delete.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# 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 UserDelete < Knife
-
- deps do
- require_relative "../user_v1"
- end
-
- banner "knife 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::UserV1, @user_name)
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_dissociate.rb b/lib/chef/knife/user_dissociate.rb
deleted file mode 100644
index 6af1559608..0000000000
--- a/lib/chef/knife/user_dissociate.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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
deleted file mode 100644
index ad9dfac079..0000000000
--- a/lib/chef/knife/user_edit.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# 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 UserEdit < Knife
-
- deps do
- require_relative "../user_v1"
- end
-
- banner "knife 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::UserV1.load(@user_name).to_hash
- 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
- end
-end
diff --git a/lib/chef/knife/user_invite_add.rb b/lib/chef/knife/user_invite_add.rb
deleted file mode 100644
index 1690147535..0000000000
--- a/lib/chef/knife/user_invite_add.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# 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
deleted file mode 100644
index 831774d1bf..0000000000
--- a/lib/chef/knife/user_invite_list.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# 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
deleted file mode 100644
index fd5804e10a..0000000000
--- a/lib/chef/knife/user_invite_rescind.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# 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
deleted file mode 100644
index efc783dd7f..0000000000
--- a/lib/chef/knife/user_key_create.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_create_base"
-
-class Chef
- class Knife
- # Implements knife user key create using Chef::Knife::KeyCreate
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the user that this key is for
- class UserKeyCreate < Knife
- include Chef::Knife::KeyCreateBase
-
- banner "knife user key create USER (options)"
-
- deps do
- require_relative "key_create"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "user"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyCreate.new(@actor, actor_field_name, ui, config)
- end
-
- def actor_missing_error
- "You must specify a user name"
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_key_delete.rb b/lib/chef/knife/user_key_delete.rb
deleted file mode 100644
index b4f84fdb7b..0000000000
--- a/lib/chef/knife/user_key_delete.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- # Implements knife user key delete using Chef::Knife::KeyDelete
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class UserKeyDelete < Knife
- banner "knife user key delete USER KEYNAME (options)"
-
- deps do
- require_relative "key_delete"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "user"
- end
-
- def actor_missing_error
- "You must specify a user name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyDelete.new(@name, @actor, actor_field_name, ui)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_key_edit.rb b/lib/chef/knife/user_key_edit.rb
deleted file mode 100644
index 15ef2ada1e..0000000000
--- a/lib/chef/knife/user_key_edit.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_edit_base"
-
-class Chef
- class Knife
- # Implements knife user key edit using Chef::Knife::KeyEdit
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the user that this key is for
- class UserKeyEdit < Knife
- include Chef::Knife::KeyEditBase
-
- banner "knife user key edit USER KEYNAME (options)"
-
- deps do
- require_relative "key_edit"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def actor_field_name
- "user"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyEdit.new(@name, @actor, actor_field_name, ui, config)
- end
-
- def actor_missing_error
- "You must specify a user name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_key_list.rb b/lib/chef/knife/user_key_list.rb
deleted file mode 100644
index 781998b301..0000000000
--- a/lib/chef/knife/user_key_list.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# 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_relative "../knife"
-require_relative "key_list_base"
-
-class Chef
- class Knife
- # Implements knife user key list using Chef::Knife::KeyList
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class UserKeyList < Knife
- include Chef::Knife::KeyListBase
-
- banner "knife user key list USER (options)"
-
- deps do
- require_relative "key_list"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def list_method
- :list_by_user
- end
-
- def actor_missing_error
- "You must specify a user name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyList.new(@actor, list_method, ui, config)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_key_show.rb b/lib/chef/knife/user_key_show.rb
deleted file mode 100644
index 2bf535c792..0000000000
--- a/lib/chef/knife/user_key_show.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# 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_relative "../knife"
-
-class Chef
- class Knife
- # Implements knife user key show using Chef::Knife::KeyShow
- # as a service class.
- #
- # @author Tyler Cloke
- #
- # @attr_reader [String] actor the name of the client that this key is for
- class UserKeyShow < Knife
- banner "knife user key show USER KEYNAME (options)"
-
- deps do
- require_relative "key_show"
- end
-
- attr_reader :actor
-
- def initialize(argv = [])
- super(argv)
- @service_object = nil
- end
-
- def run
- apply_params!(@name_args)
- service_object.run
- end
-
- def load_method
- :load_by_user
- end
-
- def actor_missing_error
- "You must specify a user name"
- end
-
- def keyname_missing_error
- "You must specify a key name"
- end
-
- def service_object
- @service_object ||= Chef::Knife::KeyShow.new(@name, @actor, load_method, ui)
- end
-
- def apply_params!(params)
- @actor = params[0]
- if @actor.nil?
- show_usage
- ui.fatal(actor_missing_error)
- exit 1
- end
- @name = params[1]
- if @name.nil?
- show_usage
- ui.fatal(keyname_missing_error)
- exit 1
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/user_list.rb b/lib/chef/knife/user_list.rb
deleted file mode 100644
index f6aa7bcfc4..0000000000
--- a/lib/chef/knife/user_list.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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 UserList < Knife
-
- deps do
- require_relative "../user_v1"
- end
-
- banner "knife user list (options)"
-
- option :with_uri,
- short: "-w",
- long: "--with-uri",
- description: "Show corresponding URIs."
-
- def run
- output(format_list_for_display(Chef::UserV1.list))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb
deleted file mode 100644
index ee58c19d9f..0000000000
--- a/lib/chef/knife/user_reregister.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# 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 UserReregister < Knife
-
- deps do
- require_relative "../user_v1"
- end
-
- banner "knife 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::UserV1.load(@user_name)
- 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
- end
-end
diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb
deleted file mode 100644
index e59f969e9a..0000000000
--- a/lib/chef/knife/user_show.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# 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 UserShow < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require_relative "../user_v1"
- end
-
- banner "knife 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::UserV1.load(@user_name)
- output(format_for_display(user))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/xargs.rb b/lib/chef/knife/xargs.rb
deleted file mode 100644
index 9dcc724d38..0000000000
--- a/lib/chef/knife/xargs.rb
+++ /dev/null
@@ -1,282 +0,0 @@
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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] (options)"
-
- category "path-based"
-
- deps do
- 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."
-
- 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]
-
- option :diff,
- 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."
-
- 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)."
-
- 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."
-
- option :replace_all,
- 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."
-
- option :max_command_line,
- 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."
-
- option :null_separator,
- short: "-0",
- boolean: true,
- description: "Use the NULL character (\0) as a separator, instead of whitespace."
-
- def run
- error = false
- # Get the matches (recursively)
- files = []
- pattern_args_from(get_patterns).each do |pattern|
- Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result|
- if result.dir?
- # TODO option to include directories
- ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it."
- else
- files << result
- ran = false
-
- # If the command would be bigger than max command line, back it off a bit
- # and run a slightly smaller command (with one less arg)
- if config[:max_command_line]
- command, tempfiles = create_command(files)
- begin
- if command.length > config[:max_command_line].to_i
- if files.length > 1
- command, tempfiles_minus_one = create_command(files[0..-2])
- begin
- error = true if xargs_files(command, tempfiles_minus_one)
- files = [ files[-1] ]
- ran = true
- ensure
- destroy_tempfiles(tempfiles)
- end
- else
- error = true if xargs_files(command, tempfiles)
- files = [ ]
- ran = true
- end
- end
- ensure
- destroy_tempfiles(tempfiles)
- end
- end
-
- # If the command has hit the limit for the # of arguments, run it
- if !ran && config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i
- command, tempfiles = create_command(files)
- begin
- error = true if xargs_files(command, tempfiles)
- files = []
- ran = true
- ensure
- destroy_tempfiles(tempfiles)
- end
- end
- end
- end
- end
-
- # Any leftovers commands shall be run
- if files.size > 0
- command, tempfiles = create_command(files)
- begin
- error = true if xargs_files(command, tempfiles)
- ensure
- destroy_tempfiles(tempfiles)
- end
- end
-
- if error
- exit 1
- end
- end
-
- def get_patterns
- if config[:patterns]
- [ config[:patterns] ].flatten
- elsif config[:null_separator]
- stdin.binmode
- stdin.read.split("\000")
- else
- stdin.read.split(/\s+/)
- end
- end
-
- def create_command(files)
- command = name_args.join(" ")
-
- # Create the (empty) tempfiles
- tempfiles = {}
- begin
- # Create the temporary files
- files.each do |file|
- tempfile = Tempfile.new(file.name)
- tempfiles[tempfile] = { file: file }
- end
- rescue
- destroy_tempfiles(files)
- raise
- end
-
- # Create the command
- paths = tempfiles.keys.map(&:path).join(" ")
- if config[:replace_all]
- final_command = command.gsub(config[:replace_all], paths)
- elsif config[:replace_first]
- final_command = command.sub(config[:replace_first], paths)
- else
- final_command = "#{command} #{paths}"
- end
-
- [final_command, tempfiles]
- end
-
- def destroy_tempfiles(tempfiles)
- # Unlink the files now that we're done with them
- tempfiles.each_key(&:close!)
- end
-
- def xargs_files(command, tempfiles)
- error = false
- # Create the temporary files
- tempfiles.each_pair do |tempfile, file|
-
- 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
-
- # Run the command
- if config[:verbose_commands] || Chef::Config[:verbosity] && Chef::Config[:verbosity] >= 1
- output sub_filenames(command, tempfiles)
- end
- command_output = `#{command}`
- command_output = sub_filenames(command_output, tempfiles)
- stdout.write command_output
-
- # Check if the output is different
- tempfiles.each_pair do |tempfile, file|
- # Read the new output
- new_value = IO.binread(tempfile.path)
-
- # Upload the output if different
- if config[:force] || new_value != file[:value]
- if config[:dry_run]
- output "Would update #{format_path(file[:file])}"
- else
- file[:file].write(new_value)
- output "Updated #{format_path(file[:file])}"
- end
- end
-
- # Print a diff of what was uploaded
- if config[:diff] && new_value != file[:value]
- old_file = Tempfile.open(file[:file].name)
- begin
- old_file.write(file[:value])
- old_file.close
-
- diff = `diff -u #{old_file.path} #{tempfile.path}`
- diff.gsub!(old_file.path, "#{format_path(file[:file])} (old)")
- diff.gsub!(tempfile.path, "#{format_path(file[:file])} (new)")
- stdout.write diff
- ensure
- old_file.close!
- end
- end
- end
-
- error
- end
-
- def sub_filenames(str, tempfiles)
- tempfiles.each_pair do |tempfile, file|
- str = str.gsub(tempfile.path, format_path(file[:file]))
- end
- str
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/yaml_convert.rb b/lib/chef/knife/yaml_convert.rb
deleted file mode 100644
index 6bd2d1c0ea..0000000000
--- a/lib/chef/knife/yaml_convert.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-#
-# 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/mixin/get_source_from_package.rb b/lib/chef/mixin/get_source_from_package.rb
index 178884c7ae..88e09758cb 100644
--- a/lib/chef/mixin/get_source_from_package.rb
+++ b/lib/chef/mixin/get_source_from_package.rb
@@ -38,7 +38,7 @@ class Chef
# 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.exist?(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
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index d569eeda38..45e8855326 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -687,6 +687,25 @@ class Chef
name <=> other.name
end
+ # Returns hash of node data with attributes based on whitelist/blacklist rules.
+ def data_for_save
+ data = for_json
+ %w{automatic default normal override}.each do |level|
+ 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
+
private
def save_without_policyfile_attrs
@@ -712,7 +731,7 @@ class Chef
# @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.deprecated(:attribute_whitelist_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]
@@ -732,24 +751,6 @@ class Chef
end
end
- def data_for_save
- data = for_json
- %w{automatic default normal override}.each do |level|
- 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
@@ -761,7 +762,7 @@ class Chef
path = File.expand_path(Chef::Config[:chef_guid_path])
dir = File.dirname(path)
- unless File.exists?(path)
+ unless File.exist?(path)
FileUtils.mkdir_p(dir)
File.write(path, SecureRandom.uuid)
end
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 29b60a98d5..6a8e72004b 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -158,8 +158,10 @@ class Chef
}.freeze
ENUM_METHODS.each do |delegated_method|
- define_method(delegated_method) do |*args, &block|
- merged_attributes.send(delegated_method, *args, &block)
+ if Hash.public_method_defined?(delegated_method)
+ define_method(delegated_method) do |*args, &block|
+ merged_attributes.send(delegated_method, *args, &block)
+ end
end
end
@@ -596,7 +598,7 @@ class Chef
merge_with.each do |key, merge_with_value|
value =
if merge_onto.key?(key)
- deep_merge!(safe_dup(merge_onto[key]), merge_with_value)
+ deep_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value)
else
merge_with_value
end
@@ -632,7 +634,7 @@ class Chef
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)
+ hash_only_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value)
else
merge_with_value
end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index 49dc0256b9..79b1983a05 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -19,6 +19,7 @@ require_relative "common_api"
require_relative "mixin/state_tracking"
require_relative "mixin/immutablize_array"
require_relative "mixin/immutablize_hash"
+require_relative "../delayed_evaluator"
class Chef
class Node
@@ -109,6 +110,12 @@ class Chef
key
end
+ def [](*args)
+ value = super
+ value = value.call while value.is_a?(::Chef::DelayedEvaluator)
+ value
+ end
+
prepend Chef::Node::Mixin::StateTracking
prepend Chef::Node::Mixin::ImmutablizeArray
end
@@ -187,6 +194,12 @@ class Chef
e
end
+ def [](*args)
+ value = super
+ value = value.call while value.is_a?(::Chef::DelayedEvaluator)
+ value
+ end
+
prepend Chef::Node::Mixin::StateTracking
prepend Chef::Node::Mixin::ImmutablizeHash
end
diff --git a/lib/chef/node/mixin/deep_merge_cache.rb b/lib/chef/node/mixin/deep_merge_cache.rb
index e88e079895..8978d77ea0 100644
--- a/lib/chef/node/mixin/deep_merge_cache.rb
+++ b/lib/chef/node/mixin/deep_merge_cache.rb
@@ -15,6 +15,8 @@
# limitations under the License.
#
+require_relative "../../delayed_evaluator"
+
class Chef
class Node
module Mixin
@@ -46,13 +48,15 @@ class Chef
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
+ ret = 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
+ ret = ret.call while ret.is_a?(::Chef::DelayedEvaluator)
+ ret
end
end
diff --git a/lib/chef/org.rb b/lib/chef/org.rb
index e2b7c49051..8f65f3ddd1 100644
--- a/lib/chef/org.rb
+++ b/lib/chef/org.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# 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");
@@ -19,9 +19,10 @@
require_relative "json_compat"
require_relative "mixin/params_validate"
require_relative "server_api"
+require_relative "group"
class Chef
- class Org
+ class Org < Group
include Chef::Mixin::ParamsValidate
diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb
index bac015be42..35282bf915 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.rb
@@ -475,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
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index bbf06854d6..0813005845 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -412,6 +412,7 @@ class Chef
# Otherwise, we have to validate it now.
value = input_to_stored_value(resource, default, is_default: true)
end
+ value = deep_dup(value)
value = stored_value_to_output(resource, value)
# If the value is mutable (non-frozen), we set it on the instance
@@ -748,5 +749,22 @@ class Chef
result
end
+
+ # recursively dup the value
+ def deep_dup(value)
+ return value if value.is_a?(DelayedEvaluator)
+
+ visitor = lambda do |obj|
+ obj = obj.dup rescue obj
+ case obj
+ when Hash
+ obj.each { |k, v| obj[k] = visitor.call(v) }
+ when Array
+ obj.each_with_index { |v, i| obj[i] = visitor.call(v) }
+ end
+ obj
+ end
+ visitor.call(value)
+ end
end
end
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 4d5631397b..81ed530fc7 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -435,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
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 7d37f34b1a..77c1973fb4 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -101,7 +101,7 @@ class Chef
if @cron_exists
unless cron_different?
- logger.trace("Skipping existing cron entry '#{new_resource.name}'")
+ logger.debug("#{new_resource}: Skipping existing cron entry")
return
end
read_crontab.each_line do |line|
diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb
index 555340d91e..6a20556ccd 100644
--- a/lib/chef/provider/directory.rb
+++ b/lib/chef/provider/directory.rb
@@ -32,7 +32,7 @@ class Chef
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
+ if ::File.exist?(current_resource.path) && @action != :create_if_missing
load_resource_attributes_from_file(current_resource)
end
current_resource
@@ -73,7 +73,7 @@ class Chef
# make sure we have write permissions to that directory
is_parent_writable = lambda do |base_dir|
base_dir = ::File.dirname(base_dir)
- if ::File.exists?(base_dir)
+ if ::File.exist?(base_dir)
if Chef::FileAccessControl.writable?(base_dir)
true
elsif Chef::Util::PathHelper.is_sip_path?(base_dir, node)
@@ -89,7 +89,7 @@ class Chef
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
- if !whyrun_mode? || ::File.exists?(parent_directory)
+ if !whyrun_mode? || ::File.exist?(parent_directory)
if Chef::FileAccessControl.writable?(parent_directory)
true
elsif Chef::Util::PathHelper.is_sip_path?(parent_directory, node)
@@ -108,7 +108,7 @@ class Chef
requirements.assert(:delete) do |a|
a.assertion do
- if ::File.exists?(new_resource.path)
+ if ::File.exist?(new_resource.path)
::File.directory?(new_resource.path) && Chef::FileAccessControl.writable?(new_resource.path)
else
true
@@ -122,7 +122,7 @@ class Chef
end
action :create do
- unless ::File.exists?(new_resource.path)
+ unless ::File.exist?(new_resource.path)
converge_by("create new directory #{new_resource.path}") do
if new_resource.recursive == true
::FileUtils.mkdir_p(new_resource.path)
@@ -138,7 +138,7 @@ class Chef
end
action :delete do
- if ::File.exists?(new_resource.path)
+ if ::File.exist?(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
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index 20b8a40bf1..b4020b8620 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -27,7 +27,7 @@ class Chef
provides :execute, target_mode: true
- def_delegators :new_resource, :command, :returns, :environment, :user, :domain, :password, :group, :cwd, :umask, :creates, :elevated, :default_env, :timeout, :input
+ def_delegators :new_resource, :command, :returns, :environment, :user, :domain, :password, :group, :cwd, :umask, :creates, :elevated, :default_env, :timeout, :input, :login
def load_current_resource
current_resource = Chef::Resource::Execute.new(new_resource.name)
@@ -92,6 +92,7 @@ class Chef
opts[:cwd] = cwd if cwd
opts[:umask] = umask if umask
opts[:input] = input if input
+ opts[:login] = login if login
opts[:default_env] = default_env
opts[:log_level] = :info
opts[:log_tag] = new_resource.to_s
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index e2c07ad9f7..59724a8340 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -152,7 +152,7 @@ class Chef
unless ::File.exist?(new_resource.path)
action_create
else
- logger.trace("#{new_resource} exists at #{new_resource.path} taking no action.")
+ logger.debug("#{new_resource} exists at #{new_resource.path} taking no action.")
end
end
@@ -338,7 +338,7 @@ class Chef
raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(tempfile_checksum))
end
- if tempfile
+ if tempfile && contents_changed?
new_resource.verify.each do |v|
unless v.verify(tempfile.path)
backupfile = "#{Chef::Config[:file_cache_path]}/failed_validations/#{::File.basename(tempfile.path)}"
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index f1c8380307..1c0b9f562e 100644
--- a/lib/chef/provider/git.rb
+++ b/lib/chef/provider/git.rb
@@ -68,9 +68,9 @@ class Chef
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}')."
+ "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|
@@ -80,8 +80,8 @@ class Chef
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}"
+ "Verify your (case-sensitive) repository URL and revision.\n" +
+ "`git ls-remote '#{new_resource.repository}' '#{rev_search_pattern}'` output: #{@resolved_reference}"
end
end
@@ -94,7 +94,7 @@ class Chef
enable_submodules
add_remotes
else
- logger.trace "#{new_resource} checkout destination #{cwd} already exists or is a non-empty directory"
+ logger.debug "#{new_resource} checkout destination #{cwd} already exists or is a non-empty directory"
end
end
@@ -281,12 +281,10 @@ class Chef
def target_revision
@target_revision ||=
- begin
- if sha_hash?(new_resource.revision)
- @target_revision = new_resource.revision
- else
- @target_revision = remote_resolve_reference
- end
+ if sha_hash?(new_resource.revision)
+ @target_revision = new_resource.revision
+ else
+ @target_revision = remote_resolve_reference
end
end
diff --git a/lib/chef/provider/group/groupadd.rb b/lib/chef/provider/group/groupadd.rb
index 63f1690f5e..b427498a8c 100644
--- a/lib/chef/provider/group/groupadd.rb
+++ b/lib/chef/provider/group/groupadd.rb
@@ -67,7 +67,7 @@ class Chef
members_to_be_added << member unless current_resource.members.include?(member)
end
members_to_be_added.each do |member|
- logger.trace("#{new_resource} appending member #{member} to group #{new_resource.group_name}")
+ logger.debug("#{new_resource} appending member #{member} to group #{new_resource.group_name}")
add_member(member)
end
end
@@ -79,13 +79,13 @@ class Chef
end
members_to_be_removed.each do |member|
- logger.trace("#{new_resource} removing member #{member} from group #{new_resource.group_name}")
+ logger.debug("#{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(", ")
- logger.trace("#{new_resource} setting group members to: #{members_description}")
+ logger.debug("#{new_resource} setting group members to: #{members_description}")
set_members(new_resource.members)
end
end
diff --git a/lib/chef/provider/group/groupmod.rb b/lib/chef/provider/group/groupmod.rb
index e4d69f34bf..d51272fcbe 100644
--- a/lib/chef/provider/group/groupmod.rb
+++ b/lib/chef/provider/group/groupmod.rb
@@ -66,13 +66,13 @@ class Chef
end
end
- logger.trace("#{new_resource} not changing group members, the group has no members to add") if members_to_be_added.empty?
+ logger.debug("#{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
- logger.trace("#{new_resource} setting group members to: none") if new_resource.members.empty?
+ logger.debug("#{new_resource} setting group members to: none") if new_resource.members.empty?
add_group_members(new_resource.members)
end
end
@@ -84,7 +84,7 @@ class Chef
# Adds a list of usernames to the group using `user mod`
def add_group_members(members)
- logger.trace("#{new_resource} adding members #{members.join(", ")}") unless members.empty?
+ logger.debug("#{new_resource} adding members #{members.join(", ")}") unless members.empty?
members.each do |user|
shell_out!("user", "mod", "-G", new_resource.group_name, user)
end
diff --git a/lib/chef/provider/group/pw.rb b/lib/chef/provider/group/pw.rb
index 0639b8fdb5..70b43478cc 100644
--- a/lib/chef/provider/group/pw.rb
+++ b/lib/chef/provider/group/pw.rb
@@ -44,7 +44,7 @@ class Chef
# 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.
- logger.trace("#{new_resource} setting group members: #{new_resource.members.join(",")}")
+ logger.debug("#{new_resource} setting group members: #{new_resource.members.join(",")}")
command += [ "-M", new_resource.members.join(",") ]
end
@@ -119,12 +119,12 @@ class Chef
end
unless members_to_be_added.empty?
- logger.trace("#{new_resource} adding group members: #{members_to_be_added.join(",")}")
+ logger.debug("#{new_resource} adding group members: #{members_to_be_added.join(",")}")
opts << [ "-m", members_to_be_added.join(",") ]
end
unless members_to_be_removed.empty?
- logger.trace("#{new_resource} removing group members: #{members_to_be_removed.join(",")}")
+ logger.debug("#{new_resource} removing group members: #{members_to_be_removed.join(",")}")
opts << [ "-d", members_to_be_removed.join(",") ]
end
diff --git a/lib/chef/provider/ifconfig.rb b/lib/chef/provider/ifconfig.rb
index d08564e75d..69d95b260c 100644
--- a/lib/chef/provider/ifconfig.rb
+++ b/lib/chef/provider/ifconfig.rb
@@ -205,7 +205,7 @@ class Chef
logger.info("#{new_resource} deleted")
end
else
- logger.trace("#{new_resource} does not exist - nothing to do")
+ logger.debug("#{new_resource} does not exist - nothing to do")
end
delete_config
end
@@ -220,7 +220,7 @@ class Chef
logger.info("#{new_resource} disabled")
end
else
- logger.trace("#{new_resource} does not exist - nothing to do")
+ logger.debug("#{new_resource} does not exist - nothing to do")
end
end
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index 900d0516af..113807e49a 100644
--- a/lib/chef/provider/link.rb
+++ b/lib/chef/provider/link.rb
@@ -73,7 +73,7 @@ class Chef
end
def canonicalize(path)
- ChefUtils.windows? ? path.tr("/", '\\') : path
+ ChefUtils.windows? ? path.tr("/", "\\") : path
end
action :create do
@@ -106,7 +106,7 @@ class Chef
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.debug("#{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
@@ -118,7 +118,7 @@ class Chef
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.debug("#{new_resource} created #{new_resource.link_type} link from #{new_resource.target_file} -> #{new_resource.to}")
logger.info("#{new_resource} created")
end
end
diff --git a/lib/chef/provider/mount.rb b/lib/chef/provider/mount.rb
index 44fb94ca01..998ea3ce9b 100644
--- a/lib/chef/provider/mount.rb
+++ b/lib/chef/provider/mount.rb
@@ -42,7 +42,7 @@ class Chef
logger.info("#{new_resource} mounted")
end
else
- logger.trace("#{new_resource} is already mounted")
+ logger.debug("#{new_resource} is already mounted")
end
end
@@ -53,7 +53,7 @@ class Chef
logger.info("#{new_resource} unmounted")
end
else
- logger.trace("#{new_resource} is already unmounted")
+ logger.debug("#{new_resource} is already unmounted")
end
end
@@ -76,7 +76,7 @@ class Chef
end
end
else
- logger.trace("#{new_resource} not mounted, nothing to remount")
+ logger.debug("#{new_resource} not mounted, nothing to remount")
end
end
@@ -87,7 +87,7 @@ class Chef
logger.info("#{new_resource} enabled")
end
else
- logger.trace("#{new_resource} already enabled")
+ logger.debug("#{new_resource} already enabled")
end
end
@@ -98,7 +98,7 @@ class Chef
logger.info("#{new_resource} disabled")
end
else
- logger.trace("#{new_resource} already disabled")
+ logger.debug("#{new_resource} already disabled")
end
end
@@ -175,8 +175,13 @@ class Chef
# 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("/")
+ # Removed "/" from the end of str unless it's a network mount, because it was causing idempotency issue.
+ device =
+ if @new_resource.device == "/" || @new_resource.device.match?(":/$")
+ @new_resource.device
+ else
+ @new_resource.device.chomp("/")
+ end
case @new_resource.device_type
when :device
device
diff --git a/lib/chef/provider/mount/aix.rb b/lib/chef/provider/mount/aix.rb
index 2cb29f5858..1a72eecbd3 100644
--- a/lib/chef/provider/mount/aix.rb
+++ b/lib/chef/provider/mount/aix.rb
@@ -140,7 +140,7 @@ class Chef
shell_out!(command)
logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
@@ -154,7 +154,7 @@ class Chef
def enable_fs
if @current_resource.enabled && mount_options_unchanged?
- logger.trace("#{@new_resource} is already enabled - nothing to do")
+ logger.debug("#{@new_resource} is already enabled - nothing to do")
return nil
end
@@ -211,7 +211,7 @@ class Chef
contents.each { |line| fstab.puts line }
end
else
- logger.trace("#{@new_resource} is not enabled - nothing to do")
+ logger.debug("#{@new_resource} is not enabled - nothing to do")
end
end
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb
index 0bd81d5453..802ee11c23 100644
--- a/lib/chef/provider/mount/mount.rb
+++ b/lib/chef/provider/mount/mount.rb
@@ -120,7 +120,7 @@ class Chef
shell_out!(*command)
logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
@@ -129,7 +129,7 @@ class Chef
shell_out!("umount", @new_resource.mount_point)
logger.trace("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
else
- logger.trace("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
+ logger.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
end
end
@@ -147,7 +147,7 @@ class Chef
sleep 1
mount_fs
else
- logger.trace("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do")
+ logger.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do")
end
end
@@ -158,7 +158,7 @@ class Chef
def enable_fs
if @current_resource.enabled && mount_options_unchanged? && device_unchanged?
- logger.trace("#{@new_resource} is already enabled - nothing to do")
+ logger.debug("#{@new_resource} is already enabled - nothing to do")
return nil
end
@@ -203,7 +203,7 @@ class Chef
end
end
# Removed "/" from the end of str, because it was causing idempotency issue.
- @real_device == "/" ? @real_device : @real_device.chomp("/")
+ (@real_device == "/" || @real_device.match?(":/$")) ? @real_device : @real_device.chomp("/")
end
def device_logstring
@@ -272,7 +272,7 @@ class Chef
contents.reverse_each { |line| fstab.puts line }
end
else
- logger.trace("#{@new_resource} is not enabled - nothing to do")
+ logger.debug("#{@new_resource} is not enabled - nothing to do")
end
end
diff --git a/lib/chef/provider/mount/windows.rb b/lib/chef/provider/mount/windows.rb
index 81e9ad6b1d..5b68417ab0 100644
--- a/lib/chef/provider/mount/windows.rb
+++ b/lib/chef/provider/mount/windows.rb
@@ -67,7 +67,7 @@ class Chef
password: @new_resource.password)
logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 6cbc8c7b24..65d2254258 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -28,14 +28,41 @@ class Chef
class Package < Chef::Provider
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
+ # 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)
#
@@ -82,7 +109,7 @@ class Chef
action :install do
unless target_version_array.any?
- logger.trace("#{new_resource} is already installed - nothing to do")
+ logger.debug("#{new_resource} is already installed - nothing to do")
return
end
@@ -111,7 +138,7 @@ class Chef
action :upgrade do
unless target_version_array.any?
- logger.trace("#{new_resource} no versions to upgrade - nothing to do")
+ logger.debug("#{new_resource} no versions to upgrade - nothing to do")
return
end
@@ -150,7 +177,7 @@ class Chef
logger.info("#{new_resource} removed")
end
else
- logger.trace("#{new_resource} package does not exist - nothing to do")
+ logger.debug("#{new_resource} package does not exist - nothing to do")
end
end
@@ -202,7 +229,7 @@ class Chef
end
end
else
- logger.trace("#{new_resource} is already locked")
+ logger.debug("#{new_resource} is already locked")
end
end
@@ -221,7 +248,7 @@ class Chef
end
end
else
- logger.trace("#{new_resource} is already unlocked")
+ logger.debug("#{new_resource} is already unlocked")
end
end
@@ -414,19 +441,21 @@ class Chef
each_package do |package_name, new_version, current_version, candidate_version|
case action
when :upgrade
- if version_equals?(current_version, new_version)
- # this is an odd use case
- logger.trace("#{new_resource} #{package_name} #{new_version} is already installed -- you are equality pinning with an :upgrade action, this may be deprecated in the future")
- target_version_array.push(nil)
- elsif version_equals?(current_version, candidate_version)
- logger.trace("#{new_resource} #{package_name} #{candidate_version} is already installed")
+ 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 !use_magic_version? && version_equals?(current_version, new_version)
+ # this is a short-circuit (mostly for the rubygems provider) 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?
logger.trace("#{new_resource} #{package_name} has no candidate_version to upgrade to")
target_version_array.push(nil)
- elsif current_version.nil?
- logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
- target_version_array.push(candidate_version)
+ elsif version_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)
@@ -436,21 +465,20 @@ class Chef
end
when :install
- if new_version
+ if new_version && !use_magic_version?
if version_requirement_satisfied?(current_version, new_version)
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)
- elsif version_equals?(current_version, candidate_version)
- logger.trace("#{new_resource} #{package_name} #{candidate_version} is already installed")
- target_version_array.push(nil)
else
logger.trace("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}")
target_version_array.push(new_version)
end
elsif 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} #{package_name} not installed, installing #{candidate_version}")
target_version_array.push(candidate_version)
else
@@ -511,8 +539,14 @@ class Chef
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
@@ -525,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
@@ -564,12 +598,10 @@ class Chef
# @return [Array] new_resource.source as an array
def source_array
@source_array ||=
- begin
- if new_resource.source.nil?
- package_name_array.map { nil }
- else
- [ new_resource.source ].flatten
- end
+ if new_resource.source.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.source ].flatten
end
end
@@ -578,16 +610,14 @@ class Chef
# @return [Array] Array of sources with package_names converted to sources
def resolved_source_array
@resolved_source_array ||=
- begin
- source_array.each_with_index.map do |source, i|
- 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)
- logger.trace("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
- package_name
- else
- source
- end
+ source_array.each_with_index.map do |source, i|
+ 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)
+ logger.trace("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
+ package_name
+ else
+ source
end
end
end
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index dbacaedb07..6f2f645ead 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -176,6 +176,7 @@ class Chef
def resolve_package_versions(pkg)
current_version = nil
candidate_version = nil
+ all_versions = []
run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line|
case line
when /^\s{2}Installed: (.+)$/
@@ -184,9 +185,34 @@ class Chef
when /^\s{2}Candidate: (.+)$/
candidate_version = ( $1 != "(none)" ) ? $1 : nil
logger.trace("#{new_resource} candidate version for #{pkg} is #{$1}")
+ when /\s+(?:\*\*\* )?(\S+) \d+/
+ all_versions << $1
end
end
- [ current_version, candidate_version ]
+ # This is a bit ugly... really this whole provider needs
+ # to be rewritten to use target_version_array and friends, but
+ # for now this gets us moving
+ idx = package_name_array.index(pkg)
+ chosen_version =
+ if idx
+ user_ver = new_version_array[idx]
+ if user_ver
+ if all_versions.include?(user_ver)
+ user_ver
+ else
+ logger.debug("User specified a version that's not available")
+ nil
+ end
+ else
+ # user didn't specify a version, use candidate
+ candidate_version
+ end
+ else
+ # this probably means we're redirected from a virtual
+ # package, so... just go with candidate version
+ candidate_version
+ end
+ [ current_version, chosen_version ]
end
def resolve_virtual_package_name(pkg)
diff --git a/lib/chef/provider/package/deb.rb b/lib/chef/provider/package/deb.rb
index 1b1b151c93..8448535303 100644
--- a/lib/chef/provider/package/deb.rb
+++ b/lib/chef/provider/package/deb.rb
@@ -28,12 +28,12 @@ class Chef
action :reconfig do
if current_resource.version.nil?
- logger.trace("#{new_resource} is NOT installed - nothing to do")
+ logger.debug("#{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")
+ logger.debug("#{new_resource} no response_file provided - nothing to do")
return
end
@@ -46,7 +46,7 @@ class Chef
logger.info("#{new_resource} reconfigured")
end
else
- logger.trace("#{new_resource} preseeding has not changed - nothing to do")
+ logger.debug("#{new_resource} preseeding has not changed - nothing to do")
end
end
diff --git a/lib/chef/provider/package/dnf.rb b/lib/chef/provider/package/dnf.rb
index 76961b5bde..5c74ad0414 100644
--- a/lib/chef/provider/package/dnf.rb
+++ b/lib/chef/provider/package/dnf.rb
@@ -34,6 +34,7 @@ class Chef
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"
@@ -71,6 +72,16 @@ class Chef
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) }
@@ -87,9 +98,15 @@ class Chef
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|
- installed_version(i).version_with_arch
+ current_version(i).version_with_arch
end
end
@@ -107,7 +124,7 @@ class Chef
alias upgrade_package install_package
def remove_package(names, versions)
- resolved_names = names.each_with_index.map { |name, i| installed_version(i).to_s unless name.nil? }
+ 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
@@ -137,10 +154,10 @@ class Chef
def resolved_package_lock_names(names)
names.each_with_index.map do |name, i|
unless name.nil?
- if installed_version(i).version.nil?
+ if magical_version(i).version.nil?
available_version(i).name
else
- installed_version(i).name
+ magical_version(i).name
end
end
end
@@ -205,14 +222,24 @@ class Chef
end
# @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]
+ 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
diff --git a/lib/chef/provider/package/dnf/dnf_helper.py b/lib/chef/provider/package/dnf/dnf_helper.py
index f4c031dec0..cfe584a142 100644
--- a/lib/chef/provider/package/dnf/dnf_helper.py
+++ b/lib/chef/provider/package/dnf/dnf_helper.py
@@ -64,7 +64,7 @@ def version_tuple(versionstr):
tmp = versionstr[colon_index + 1:dash_index]
if tmp != '':
v = tmp
- arch_index = versionstr.find('.', dash_index)
+ arch_index = versionstr.rfind('.', dash_index)
if arch_index > 0:
r = versionstr[dash_index + 1:arch_index]
else:
@@ -78,12 +78,12 @@ def version_tuple(versionstr):
def versioncompare(versions):
sack = get_sack()
if (versions[0] is None) or (versions[1] is None):
- outpipe.write('0\n')
- outpipe.flush()
+ 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()
+ 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()
@@ -98,14 +98,27 @@ def query(command):
q = q.available()
if 'epoch' in command:
- q = q.filterm(epoch=int(command['epoch']))
+ # 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:
- q = q.filterm(version__glob=command['version'])
+ 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:
- q = q.filterm(release__glob=command['release'])
+ 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:
- q = q.filterm(arch__glob=command['arch'])
+ 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() ])
@@ -139,22 +152,27 @@ def setup_exit_handler():
signal.signal(signal.SIGQUIT, exit_handler)
if len(sys.argv) < 3:
- inpipe = sys.stdin
- outpipe = sys.stdout
+ inpipe = sys.stdin
+ outpipe = sys.stdout
else:
- inpipe = os.fdopen(int(sys.argv[1]), "r")
- outpipe = os.fdopen(int(sys.argv[2]), "w")
+ os.set_blocking(int(sys.argv[1]), True)
+ inpipe = os.fdopen(int(sys.argv[1]), "r")
+ outpipe = os.fdopen(int(sys.argv[2]), "w")
try:
+ setup_exit_handler()
while 1:
- # kill self if we get orphaned (tragic)
+ # stop the process if the parent proc goes away
ppid = os.getppid()
if ppid == 1:
raise RuntimeError("orphaned")
- setup_exit_handler()
line = inpipe.readline()
+ # only way to detect EOF in python
+ if line == "":
+ break
+
try:
command = json.loads(line)
except ValueError:
@@ -170,4 +188,4 @@ try:
raise RuntimeError("bad command")
finally:
if base is not None:
- base.closeRpmDB()
+ base.close()
diff --git a/lib/chef/provider/package/dnf/python_helper.rb b/lib/chef/provider/package/dnf/python_helper.rb
index 9dfcc7808a..b774c9a269 100644
--- a/lib/chef/provider/package/dnf/python_helper.rb
+++ b/lib/chef/provider/package/dnf/python_helper.rb
@@ -42,17 +42,16 @@ class Chef
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 = 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
+ "#{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)
@@ -75,6 +74,7 @@ class Chef
stderr.close unless stderr.nil?
inpipe.close unless inpipe.nil?
outpipe.close unless outpipe.nil?
+ @stdin = @stdout = @stderr = @inpipe = @outpipe = @wait_thr = nil
end
end
@@ -114,7 +114,7 @@ class Chef
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?
+ reap unless repo_opts.empty?
version
end
@@ -148,10 +148,11 @@ class Chef
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
+ outpipe.puts json
+ outpipe.flush
+ output = inpipe.readline.chomp
Chef::Log.trace "got '#{output}' from python helper"
- return output
+ output
end
end
@@ -199,13 +200,13 @@ class Chef
Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
end
ret
- rescue EOFError, Errno::EPIPE, Timeout::Error, Errno::ESRCH => e
+ rescue => e
output = drain_fds
- if ( max_retries -= 1 ) > 0
+ restart
+ if ( max_retries -= 1 ) > 0 && !ENV["DNF_HELPER_NO_RETRIES"]
unless output.empty?
Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
end
- restart
retry
else
raise e if output.empty?
diff --git a/lib/chef/provider/package/freebsd/pkgng.rb b/lib/chef/provider/package/freebsd/pkgng.rb
index 87acb3a830..077464b5c2 100644
--- a/lib/chef/provider/package/freebsd/pkgng.rb
+++ b/lib/chef/provider/package/freebsd/pkgng.rb
@@ -42,7 +42,9 @@ class Chef
end
def current_installed_version
- pkg_info = shell_out!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 70])
+ # 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
diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb
index 3182686c0e..9975010e5b 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -28,13 +28,13 @@ class Chef
provides :package, platform: "gentoo"
provides :portage_package
- PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}.freeze
+ PACKAGE_NAME_PATTERN = %r{^(?:([^/]+)/)?([^/]+)$}.freeze
def load_current_resource
@current_resource = Chef::Resource::PortagePackage.new(new_resource.name)
current_resource.package_name(new_resource.package_name)
- category, pkg = /^#{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)
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index 9ac1e7d31b..1a396a364b 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -72,7 +72,6 @@ class Chef
# 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
#
# @return [Gem::Specification]
#
@@ -107,10 +106,8 @@ class Chef
# 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
- elsif rubygems_version >= Gem::Version.new("1.8.0")
+ 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
@@ -192,7 +189,8 @@ class Chef
begin
rs = dependency_installer.resolve_dependencies gem_dependency.name, gem_dependency.requirement
rs.specs.find { |s| s.name == gem_dependency.name }
- rescue Gem::UnsatisfiableDependencyError
+ # ruby-3.0.0 versions of rubygems-3.x throws NoMethodError when the dep is not found
+ rescue Gem::UnsatisfiableDependencyError, NoMethodError
nil
end
end
@@ -423,11 +421,11 @@ class Chef
end
def is_omnibus?
- if %r{/(opscode|chef|chefdk)/embedded/bin}.match?(RbConfig::CONFIG["bindir"])
+ if %r{/(#{ChefUtils::Dist::Org::LEGACY_CONF_DIR}|#{ChefUtils::Dist::Infra::SHORT}|#{ChefUtils::Dist::Workstation::DIR_SUFFIX})/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"
+ elsif RbConfig::CONFIG["bindir"].sub(/^\w:/, "") == "/#{ChefUtils::Dist::Org::LEGACY_CONF_DIR}/#{ChefUtils::Dist::Infra::SHORT}/embedded/bin"
logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG["bindir"]}")
# windows, with the drive letter removed
true
@@ -481,9 +479,7 @@ class Chef
end
def all_installed_versions
- @all_installed_versions ||= begin
- @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, ">= 0"))
- end
+ @all_installed_versions ||= @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, ">= 0"))
end
##
@@ -523,13 +519,11 @@ class Chef
end
def candidate_version
- @candidate_version ||= begin
- if source_is_remote?
- @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
- else
- @gem_env.candidate_version_from_file(gem_dependency, new_resource.source).to_s
- end
- end
+ @candidate_version ||= if source_is_remote?
+ @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
+ else
+ @gem_env.candidate_version_from_file(gem_dependency, new_resource.source).to_s
+ end
end
def version_requirement_satisfied?(current_version, new_version)
diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb
index c722d8222c..4350eb6d12 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -70,8 +70,7 @@ class Chef
end
def package_provider
- @package_provider ||= begin
- case installer_type
+ @package_provider ||= case installer_type
when :msi
logger.trace("#{new_resource} is MSI")
require_relative "windows/msi"
@@ -80,8 +79,7 @@ class Chef
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
+ end
end
def installer_type
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 76b9b15172..121083ea46 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -237,11 +237,8 @@ class Chef
@installed_version[index]
end
- # cache flushing is accomplished by simply restarting the python helper. this produces a roughly
- # 15% hit to the runtime of installing/removing/upgrading packages. correctly using multipackage
- # array installs (and the multipackage cookbook) can produce 600% improvements in runtime.
def flushcache
- python_helper.restart
+ python_helper.close_rpmdb
end
def yum_binary
diff --git a/lib/chef/provider/package/yum/python_helper.rb b/lib/chef/provider/package/yum/python_helper.rb
index 7758383b95..db929ea88b 100644
--- a/lib/chef/provider/package/yum/python_helper.rb
+++ b/lib/chef/provider/package/yum/python_helper.rb
@@ -51,7 +51,6 @@ class Chef
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)
@@ -74,6 +73,7 @@ class Chef
stderr.close unless stderr.nil?
inpipe.close unless inpipe.nil?
outpipe.close unless outpipe.nil?
+ @stdin = @stdout = @stderr = @inpipe = @outpipe = @wait_thr = nil
end
end
@@ -81,6 +81,10 @@ class Chef
start if stdin.nil?
end
+ def close_rpmdb
+ query("close_rpmdb", {})
+ end
+
def compare_versions(version1, version2)
query("versioncompare", { "versions" => [version1, version2] }).to_i
end
@@ -117,12 +121,12 @@ class Chef
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?
+ # XXX: for now we close the rpmdb before and after every query with an enablerepo/disablerepo to clean the helpers internal state
+ close_rpmdb 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?
+ close_rpmdb unless repo_opts.empty?
version
end
@@ -156,10 +160,11 @@ class Chef
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
+ outpipe.puts json
+ outpipe.flush
+ output = inpipe.readline.chomp
Chef::Log.trace "got '#{output}' from python helper"
- return output
+ output
end
end
@@ -207,13 +212,13 @@ class Chef
Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
end
ret
- rescue EOFError, Errno::EPIPE, Timeout::Error, Errno::ESRCH => e
+ rescue => e
output = drain_fds
- if ( max_retries -= 1 ) > 0
+ restart
+ if ( max_retries -= 1 ) > 0 && !ENV["YUM_HELPER_NO_RETRIES"]
unless output.empty?
Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
end
- restart
retry
else
raise e if output.empty?
diff --git a/lib/chef/provider/package/yum/simplejson/LICENSE.txt b/lib/chef/provider/package/yum/simplejson/LICENSE.txt
deleted file mode 100644
index e05f49c3fd..0000000000
--- a/lib/chef/provider/package/yum/simplejson/LICENSE.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-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
deleted file mode 100644
index d5b4d39913..0000000000
--- a/lib/chef/provider/package/yum/simplejson/__init__.py
+++ /dev/null
@@ -1,318 +0,0 @@
-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
deleted file mode 100644
index 10679d3b04..0000000000
--- a/lib/chef/provider/package/yum/simplejson/__init__.pyc
+++ /dev/null
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/decoder.py b/lib/chef/provider/package/yum/simplejson/decoder.py
deleted file mode 100644
index d921ce0b97..0000000000
--- a/lib/chef/provider/package/yum/simplejson/decoder.py
+++ /dev/null
@@ -1,354 +0,0 @@
-"""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
deleted file mode 100644
index d402901870..0000000000
--- a/lib/chef/provider/package/yum/simplejson/decoder.pyc
+++ /dev/null
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/encoder.py b/lib/chef/provider/package/yum/simplejson/encoder.py
deleted file mode 100644
index cf58290366..0000000000
--- a/lib/chef/provider/package/yum/simplejson/encoder.py
+++ /dev/null
@@ -1,440 +0,0 @@
-"""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
deleted file mode 100644
index 207bce5cfb..0000000000
--- a/lib/chef/provider/package/yum/simplejson/encoder.pyc
+++ /dev/null
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/scanner.py b/lib/chef/provider/package/yum/simplejson/scanner.py
deleted file mode 100644
index adbc6ec979..0000000000
--- a/lib/chef/provider/package/yum/simplejson/scanner.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""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
deleted file mode 100644
index 12df070e44..0000000000
--- a/lib/chef/provider/package/yum/simplejson/scanner.pyc
+++ /dev/null
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/tool.py b/lib/chef/provider/package/yum/simplejson/tool.py
deleted file mode 100644
index 90443317b2..0000000000
--- a/lib/chef/provider/package/yum/simplejson/tool.py
+++ /dev/null
@@ -1,37 +0,0 @@
-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/yum_helper.py b/lib/chef/provider/package/yum/yum_helper.py
index 47cbe2efe6..4dfe0cb24b 100644
--- a/lib/chef/provider/package/yum/yum_helper.py
+++ b/lib/chef/provider/package/yum/yum_helper.py
@@ -2,45 +2,26 @@
# 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.
+# NOTE: this actually needs to run under python2.7 and centos 6.x through python3 and centos 7.x
+# please manually test changes on centos6 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 fcntl
+import 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))
+from yum.misc import string_to_prco_tuple
# 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]
@@ -51,9 +32,9 @@ def versioncompare(versions):
# 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,"")
+ final_version1 = versions[0].replace("." + candidate_arch1,"")
else:
- final_version1 = versions[0]
+ final_version1 = versions[0]
final_version2 = versions[1]
@@ -64,37 +45,24 @@ def versioncompare(versions):
outpipe.write("%(e)s\n" % { 'e': evr_comparison })
outpipe.flush()
-def install_only_packages(name):
- base = get_base()
+def install_only_packages(base, name):
if name in base.conf.installonlypkgs:
- outpipe.write('True')
+ outpipe.write('True\n')
else:
- outpipe.write('False')
+ outpipe.write('False\n')
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()
-
+def query(base, command):
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'])
+ for repo in command['repos']:
+ if 'enable' in repo:
+ base.repos.enableRepo(repo['enable'])
if 'disable' in repo:
- base.repos.disableRepo(repo['disable'])
+ base.repos.disableRepo(repo['disable'])
args = { 'name': command['provides'] }
do_nevra = False
@@ -136,7 +104,7 @@ def query(command):
# 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)
+ pkgs = obj.searchNevra(name=args['name'], arch=desired_arch)
else:
pats = [command['provides']]
pkgs = obj.returnPackages(patterns=pats)
@@ -157,13 +125,13 @@ def query(command):
# 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'])
+ 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'])
+ 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
@@ -179,36 +147,56 @@ def setup_exit_handler():
signal.signal(signal.SIGPIPE, exit_handler)
signal.signal(signal.SIGQUIT, exit_handler)
+def set_blocking(fd):
+ old_flags = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, old_flags & ~os.O_NONBLOCK)
+
+base = None
+
if len(sys.argv) < 3:
- inpipe = sys.stdin
- outpipe = sys.stdout
+ inpipe = sys.stdin
+ outpipe = sys.stdout
else:
- inpipe = os.fdopen(int(sys.argv[1]), "r")
- outpipe = os.fdopen(int(sys.argv[2]), "w")
+ set_blocking(int(sys.argv[1]))
+ set_blocking(int(sys.argv[2]))
+ inpipe = os.fdopen(int(sys.argv[1]), "r")
+ outpipe = os.fdopen(int(sys.argv[2]), "w")
try:
+ setup_exit_handler()
while 1:
- # kill self if we get orphaned (tragic)
+ # stop the process if the parent proc goes away
ppid = os.getppid()
if ppid == 1:
raise RuntimeError("orphaned")
- setup_exit_handler()
line = inpipe.readline()
+ # only way to detect EOF in python
+ if line == "":
+ break
+
try:
command = json.loads(line)
except ValueError, e:
raise RuntimeError("bad json parse")
+ if base is None:
+ base = yum.YumBase()
+
if command['action'] == "whatinstalled":
- query(command)
+ query(base, command)
elif command['action'] == "whatavailable":
- query(command)
+ query(base, command)
elif command['action'] == "versioncompare":
versioncompare(command['versions'])
elif command['action'] == "installonlypkgs":
- install_only_packages(command['package'])
+ install_only_packages(base, command['package'])
+ elif command['action'] == "close_rpmdb":
+ base.closeRpmDB()
+ base = None
+ outpipe.write('nil nil nil\n')
+ outpipe.flush()
else:
raise RuntimeError("bad command")
finally:
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index 316a2a1081..5d723feb29 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -78,7 +78,7 @@ class Chef
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.failure_message(Chef::Exceptions::Win32RegHiveMissing, "Hive #{new_resource.key.split("\\").shift} does not exist")
end
requirements.assert(:create) do |a|
diff --git a/lib/chef/provider/route.rb b/lib/chef/provider/route.rb
index 614d56aa22..aa4acc1f83 100644
--- a/lib/chef/provider/route.rb
+++ b/lib/chef/provider/route.rb
@@ -131,7 +131,7 @@ class Chef
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")
+ logger.debug("#{new_resource} route already active - nothing to do")
else
command = generate_command(:add)
converge_by("run #{command.join(" ")} to add route") do
@@ -152,7 +152,7 @@ class Chef
logger.info("#{new_resource} removed")
end
else
- logger.trace("#{new_resource} route does not exist - nothing to do")
+ logger.debug("#{new_resource} route does not exist - nothing to do")
end
# for now we always write the file (ugly but its what it is)
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index 7a2d2fc86c..a1a43ef54e 100644
--- a/lib/chef/provider/service.rb
+++ b/lib/chef/provider/service.rb
@@ -82,7 +82,7 @@ class Chef
action :enable do
if current_resource.enabled
- logger.trace("#{new_resource} already enabled - nothing to do")
+ logger.debug("#{new_resource} already enabled - nothing to do")
else
converge_by("enable service #{new_resource}") do
enable_service
@@ -100,7 +100,7 @@ class Chef
logger.info("#{new_resource} disabled")
end
else
- logger.trace("#{new_resource} already disabled - nothing to do")
+ logger.debug("#{new_resource} already disabled - nothing to do")
end
load_new_resource_state
new_resource.enabled(false)
@@ -108,7 +108,7 @@ class Chef
action :mask do
if current_resource.masked
- logger.trace("#{new_resource} already masked - nothing to do")
+ logger.debug("#{new_resource} already masked - nothing to do")
else
converge_by("mask service #{new_resource}") do
mask_service
@@ -126,7 +126,7 @@ class Chef
logger.info("#{new_resource} unmasked")
end
else
- logger.trace("#{new_resource} already unmasked - nothing to do")
+ logger.debug("#{new_resource} already unmasked - nothing to do")
end
load_new_resource_state
new_resource.masked(false)
@@ -139,7 +139,7 @@ class Chef
logger.info("#{new_resource} started")
end
else
- logger.trace("#{new_resource} already running - nothing to do")
+ logger.debug("#{new_resource} already running - nothing to do")
end
load_new_resource_state
new_resource.running(true)
@@ -152,7 +152,7 @@ class Chef
logger.info("#{new_resource} stopped")
end
else
- logger.trace("#{new_resource} already stopped - nothing to do")
+ logger.debug("#{new_resource} already stopped - nothing to do")
end
load_new_resource_state
new_resource.running(false)
diff --git a/lib/chef/provider/service/aixinit.rb b/lib/chef/provider/service/aixinit.rb
index e845629fe7..0241839f20 100644
--- a/lib/chef/provider/service/aixinit.rb
+++ b/lib/chef/provider/service/aixinit.rb
@@ -45,7 +45,7 @@ class Chef
priority_ok = @current_resource.priority == @new_resource.priority
end
if @current_resource.enabled && priority_ok
- logger.trace("#{@new_resource} already enabled - nothing to do")
+ logger.debug("#{@new_resource} already enabled - nothing to do")
else
converge_by("enable service #{@new_resource}") do
enable_service
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index 17e0b19b9c..7e38b7072b 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -138,7 +138,7 @@ class Chef
priority_ok = @current_resource.priority == new_resource.priority
end
if current_resource.enabled && priority_ok
- logger.trace("#{new_resource} already enabled - nothing to do")
+ logger.debug("#{new_resource} already enabled - nothing to do")
else
converge_by("enable service #{new_resource}") do
enable_service
diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb
index 01f7adef9f..9c1cdba39f 100644
--- a/lib/chef/provider/service/freebsd.rb
+++ b/lib/chef/provider/service/freebsd.rb
@@ -130,27 +130,21 @@ class Chef
# The variable name used in /etc/rc.conf for enabling this service
def service_enable_variable_name
@service_enable_variable_name ||=
- begin
- # Look for name="foo" in the shell script @init_command. Use this for determining the variable name in /etc/rc.conf
- # corresponding to this service
- # For example: to enable the service mysql-server with the init command /usr/local/etc/rc.d/mysql-server, you need
- # to set mysql_enable="YES" in /etc/rc.conf$
- if init_command
- ::File.open(init_command) do |rcscript|
- rcscript.each_line do |line|
- if line =~ /^name="?(\w+)"?/
- return $1 + "_enable"
- end
+ if init_command
+ ::File.open(init_command) do |rcscript|
+ rcscript.each_line do |line|
+ if line =~ /^name="?(\w+)"?/
+ return $1 + "_enable"
end
end
- # some scripts support multiple instances through symlinks such as openvpn.
- # We should get the service name from 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
- new_resource.service_name
end
+ # some scripts support multiple instances through symlinks such as openvpn.
+ # We should get the service name from 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
+ new_resource.service_name
end
end
@@ -161,9 +155,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
@@ -171,7 +165,7 @@ class Chef
end
if current_resource.enabled.nil?
- logger.trace("#{new_resource.name} enable/disable state unknown")
+ logger.debug("#{new_resource.name} enable/disable state unknown")
current_resource.enabled false
end
end
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
index 2152789a6e..fd8610a0f9 100644
--- a/lib/chef/provider/service/macosx.rb
+++ b/lib/chef/provider/service/macosx.rb
@@ -105,7 +105,7 @@ class Chef
def start_service
if @current_resource.running
- logger.trace("#{@new_resource} already running, not starting")
+ logger.debug("#{@new_resource} already running, not starting")
else
if @new_resource.start_command
super
@@ -117,7 +117,7 @@ class Chef
def stop_service
unless @current_resource.running
- logger.trace("#{@new_resource} not running, not stopping")
+ logger.debug("#{@new_resource} not running, not stopping")
else
if @new_resource.stop_command
super
@@ -153,7 +153,7 @@ class Chef
#
def enable_service
if @current_resource.enabled
- logger.trace("#{@new_resource} already enabled, not enabling")
+ logger.debug("#{@new_resource} already enabled, not enabling")
else
load_service
end
@@ -161,7 +161,7 @@ class Chef
def disable_service
unless @current_resource.enabled
- logger.trace("#{@new_resource} not enabled, not disabling")
+ logger.debug("#{@new_resource} not enabled, not disabling")
else
unload_service
end
@@ -169,12 +169,12 @@ class Chef
def load_service
session = @session_type ? "-S #{@session_type} " : ""
- cmd = "launchctl load -w " + session + @plist
+ cmd = "/bin/launchctl load -w " + session + @plist
shell_out_as_user(cmd)
end
def unload_service
- cmd = "launchctl unload -w " + @plist
+ cmd = "/bin/launchctl unload -w " + @plist
shell_out_as_user(cmd)
end
@@ -190,7 +190,7 @@ class Chef
def set_service_status
return if @plist.nil? || @service_label.to_s.empty?
- cmd = "launchctl list #{@service_label}"
+ cmd = "/bin/launchctl list #{@service_label}"
res = shell_out_as_user(cmd)
if res.exitstatus == 0
diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb
index 11c8d285bc..8f785875cc 100644
--- a/lib/chef/provider/service/systemd.rb
+++ b/lib/chef/provider/service/systemd.rb
@@ -76,6 +76,30 @@ 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
raise NotImplementedError, "#{new_resource} does not support the user property on a target_mode host (yet)" if Chef::Config.target_mode?
@@ -98,7 +122,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def start_service
if current_resource.running
- logger.trace("#{new_resource} already running, not starting")
+ logger.debug("#{new_resource} already running, not starting")
else
if new_resource.start_command
super
@@ -111,7 +135,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def stop_service
unless current_resource.running
- logger.trace("#{new_resource} not running, not stopping")
+ logger.debug("#{new_resource} not running, not stopping")
else
if new_resource.stop_command
super
@@ -146,7 +170,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def enable_service
if current_resource.masked || current_resource.indirect
- logger.trace("#{new_resource} cannot be enabled: it is masked or indirect")
+ logger.debug("#{new_resource} cannot be enabled: it is masked or indirect")
return
end
options, args = get_systemctl_options_args
@@ -155,7 +179,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def disable_service
if current_resource.masked || current_resource.indirect
- logger.trace("#{new_resource} cannot be disabled: it is masked or indirect")
+ logger.debug("#{new_resource} cannot be disabled: it is masked or indirect")
return
end
options, args = get_systemctl_options_args
@@ -173,25 +197,30 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
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
+ # if the service is in sysv compat mode, shellout to determine if enabled
+ if systemd_service_status["UnitFileState"] == "bad"
+ options, args = get_systemctl_options_args
+ return shell_out(systemctl_path, args, "is-enabled", new_resource.service_name, "--quiet", **options).exitstatus == 0
+ end
+ # 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?
- options, args = get_systemctl_options_args
- s = shell_out(systemctl_path, args, "is-enabled", new_resource.service_name, **options)
- s.stdout.include?("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 2b9e304160..7c2b82dc3a 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -28,10 +28,6 @@ class Chef
# 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 = %r{\S+ \(?(start|stop)?\)? ?[/ ](\w+)}.freeze
# Returns true if the configs for the service name has upstart variable
@@ -65,15 +61,8 @@ class Chef
end
end
- platform, version = Chef::Platform.find_platform_and_version(run_context.node)
- if platform == "ubuntu" && (8.04..9.04).cover?(version.to_f)
- @upstart_job_dir = "/etc/event.d"
- @upstart_conf_suffix = ""
- else
- @upstart_job_dir = "/etc/init"
- @upstart_conf_suffix = ".conf"
- end
-
+ @upstart_job_dir = "/etc/init"
+ @upstart_conf_suffix = ".conf"
@command_success = true # new_resource.status_command= false, means upstart used
@config_file_found = true
@upstart_command_success = true
diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb
index 98aad4fe29..ba2ecf224c 100644
--- a/lib/chef/provider/service/windows.rb
+++ b/lib/chef/provider/service/windows.rb
@@ -85,7 +85,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
state = current_state
if state == RUNNING
- logger.trace "#{@new_resource} already started - nothing to do"
+ logger.debug "#{@new_resource} already started - nothing to do"
elsif state == START_PENDING
logger.trace "#{@new_resource} already sent start signal - waiting for start"
wait_for_state(RUNNING)
@@ -114,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
- logger.trace "#{@new_resource} does not exist - nothing to do"
+ logger.debug "#{@new_resource} does not exist - nothing to do"
end
end
@@ -133,7 +133,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
@new_resource.updated_by_last_action(true)
elsif state == STOPPED
- logger.trace "#{@new_resource} already stopped - nothing to do"
+ logger.debug "#{@new_resource} already stopped - nothing to do"
elsif state == STOP_PENDING
logger.trace "#{@new_resource} already sent stop signal - waiting for stop"
wait_for_state(STOPPED)
@@ -141,7 +141,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
raise Chef::Exceptions::Service, "Service #{@new_resource} can't be stopped from state [#{state}]"
end
else
- logger.trace "#{@new_resource} does not exist - nothing to do"
+ logger.debug "#{@new_resource} does not exist - nothing to do"
end
end
@@ -156,7 +156,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
@new_resource.updated_by_last_action(true)
else
- logger.trace "#{@new_resource} does not exist - nothing to do"
+ logger.debug "#{@new_resource} does not exist - nothing to do"
end
end
@@ -164,7 +164,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
if Win32::Service.exists?(@new_resource.service_name)
set_startup_type(:automatic)
else
- logger.trace "#{@new_resource} does not exist - nothing to do"
+ logger.debug "#{@new_resource} does not exist - nothing to do"
end
end
@@ -172,13 +172,13 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
if Win32::Service.exists?(@new_resource.service_name)
set_startup_type(:disabled)
else
- logger.trace "#{@new_resource} does not exist - nothing to do"
+ logger.debug "#{@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"
+ logger.debug "#{new_resource} already exists - nothing to do"
return
end
@@ -191,7 +191,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
action :delete do
unless Win32::Service.exists?(new_resource.service_name)
- logger.trace "#{new_resource} does not exist - nothing to do"
+ logger.debug "#{new_resource} does not exist - nothing to do"
return
end
@@ -222,7 +222,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
logger.info("#{@new_resource} enabled")
end
else
- logger.trace("#{@new_resource} already enabled - nothing to do")
+ logger.debug("#{@new_resource} already enabled - nothing to do")
end
load_new_resource_state
@new_resource.enabled(true)
@@ -235,7 +235,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
logger.info("#{@new_resource} disabled")
end
else
- logger.trace("#{@new_resource} already disabled - nothing to do")
+ logger.debug("#{@new_resource} already disabled - nothing to do")
end
load_new_resource_state
@new_resource.enabled(false)
@@ -248,7 +248,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
set_startup_type(startup_type)
end
else
- logger.trace("#{@new_resource} startup_type already #{startup_type} - nothing to do")
+ logger.debug("#{@new_resource} startup_type already #{startup_type} - nothing to do")
end
converge_delayed_start
diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb
index 18fc9d3a3c..7f49e2139f 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -61,7 +61,7 @@ class Chef
shell_out!(checkout_command, run_options)
end
else
- logger.trace "#{new_resource} checkout destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
+ logger.debug "#{new_resource} checkout destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
end
end
@@ -69,7 +69,7 @@ class Chef
if target_dir_non_existent_or_empty?
action_force_export
else
- logger.trace "#{new_resource} export destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
+ logger.debug "#{new_resource} export destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
end
end
@@ -121,16 +121,14 @@ class Chef
# up the revision id by asking the server
# If the specified revision is an integer, trust it.
def revision_int
- @revision_int ||= begin
- 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
-
- extract_revision_info(svn_info)
- end
- end
+ @revision_int ||= 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
+
+ extract_revision_info(svn_info)
+ end
end
alias :revision_slug :revision_int
diff --git a/lib/chef/provider/support/zypper_repo.erb b/lib/chef/provider/support/zypper_repo.erb
index 6d508fa77f..23e871e406 100644
--- a/lib/chef/provider/support/zypper_repo.erb
+++ b/lib/chef/provider/support/zypper_repo.erb
@@ -1,15 +1,17 @@
-# This file was generated by Chef
+# This file was generated by Chef Infra
# 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? -%>
+<% next if @config.send(prop.to_sym).nil? || (@config.send(prop.to_sym).is_a?(Array) && @config.send(prop.to_sym).empty?) -%>
<%= prop %>=<%=
case @config.send(prop.to_sym)
when TrueClass
'1'
when FalseClass
'0'
+ when Array
+ @config.send(prop.to_sym).join("\n ")
else
@config.send(prop.to_sym)
end %>
diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb
index 97043e48c7..26a20814f5 100644
--- a/lib/chef/provider/systemd_unit.rb
+++ b/lib/chef/provider/systemd_unit.rb
@@ -55,6 +55,26 @@ class Chef
end
end
+ def systemd_unit_status
+ @systemd_unit_status ||= begin
+ # Collect all the status information for a unit and return it at once
+ # This may fail if we are managing a template unit (e.g. with '@'), in which case
+ # we just ignore the error because unit status is irrelevant in that case
+ s = shell_out(*systemctl_cmd, "show", "-p", "UnitFileState", "-p", "ActiveState", new_resource.unit_name, **systemctl_opts)
+ # e.g. /bin/systemctl --system show -p UnitFileState -p ActiveState syslog.socket
+ # Returns something like:
+ # ActiveState=inactive
+ # UnitFileState=static
+ status = {}
+ s.stdout.each_line do |line|
+ k, v = line.strip.split("=")
+ status[k] = v
+ end
+
+ status
+ end
+ end
+
action :create do
if current_resource.content != new_resource.to_ini
converge_by("creating unit: #{new_resource.unit_name}") do
@@ -87,10 +107,10 @@ class Chef
action :enable do
if current_resource.static
- logger.trace("#{new_resource.unit_name} is a static unit, enabling is a NOP.")
+ logger.debug("#{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.")
+ logger.debug("#{new_resource.unit_name} is an indirect unit, enabling is a NOP.")
end
unless current_resource.enabled || current_resource.static || current_resource.indirect
@@ -103,11 +123,11 @@ class Chef
action :disable do
if current_resource.static
- logger.trace("#{new_resource.unit_name} is a static unit, disabling is a NOP.")
+ logger.debug("#{new_resource.unit_name} is a static unit, disabling is a NOP.")
end
if current_resource.indirect
- logger.trace("#{new_resource.unit_name} is an indirect unit, enabling is a NOP.")
+ logger.debug("#{new_resource.unit_name} is an indirect unit, enabling is a NOP.")
end
if current_resource.enabled && !current_resource.static && !current_resource.indirect
@@ -175,7 +195,7 @@ class Chef
logger.info("#{new_resource} reloaded")
end
else
- logger.trace("#{new_resource.unit_name} is not active, skipping reload.")
+ logger.debug("#{new_resource.unit_name} is not active, skipping reload.")
end
end
@@ -201,23 +221,29 @@ class Chef
end
def active?
- systemctl_execute("is-active", new_resource.unit_name).exitstatus == 0
+ # Note: "activating" is not active (as with type=notify or a oneshot)
+ systemd_unit_status["ActiveState"] == "active"
end
def enabled?
- systemctl_execute("is-enabled", new_resource.unit_name).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_unit_status["UnitFileState"])
end
def masked?
- systemctl_execute("status", new_resource.unit_name).stdout.include?("masked")
+ # Note: masked-runtime is excluded, because runtime is volatile, and
+ # because masked-runtime is not masked.
+ systemd_unit_status["UnitFileState"] == "masked"
end
def static?
- systemctl_execute("is-enabled", new_resource.unit_name).stdout.include?("static")
+ systemd_unit_status["UnitFileState"] == "static"
end
def indirect?
- systemctl_execute("is-enabled", new_resource.unit_name).stdout.include?("indirect")
+ systemd_unit_status["UnitFileState"] == "indirect"
end
private
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index a0977b5523..00a82fea79 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -29,9 +29,7 @@ class Chef
include Chef::Mixin::Template
def template_location
- @template_file_cache_location ||= begin
- template_finder.find(new_resource.source, local: new_resource.local, cookbook: new_resource.cookbook)
- end
+ @template_file_cache_location ||= template_finder.find(new_resource.source, local: new_resource.local, cookbook: new_resource.cookbook)
end
private
@@ -65,7 +63,7 @@ class Chef
context[:template_finder] = template_finder
# helper variables
- context[:cookbook_name] = new_resource.cookbook_name unless context.keys.include?(:coookbook_name)
+ context[:cookbook_name] = new_resource.cookbook_name unless context.keys.include?(:cookbook_name)
context[:recipe_name] = new_resource.recipe_name unless context.keys.include?(:recipe_name)
context[:recipe_line_string] = new_resource.source_line unless context.keys.include?(:recipe_line_string)
context[:recipe_path] = new_resource.source_line_file unless context.keys.include?(:recipe_path)
@@ -84,9 +82,7 @@ class Chef
end
def template_finder
- @template_finder ||= begin
- TemplateFinder.new(run_context, new_resource.cookbook_name, run_context.node)
- end
+ @template_finder ||= TemplateFinder.new(run_context, new_resource.cookbook_name, run_context.node)
end
end
end
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 803d87d820..61de2127bb 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -172,7 +172,7 @@ class Chef
logger.info("#{new_resource} locked")
end
else
- logger.trace("#{new_resource} already locked - nothing to do")
+ logger.debug("#{new_resource} already locked - nothing to do")
end
end
@@ -183,7 +183,7 @@ class Chef
logger.info("#{new_resource} unlocked")
end
else
- logger.trace("#{new_resource} already unlocked - nothing to do")
+ logger.debug("#{new_resource} already unlocked - nothing to do")
end
end
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 7b266b8d62..cd10403e4a 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -438,7 +438,7 @@ in 'password', with the associated 'salt' and 'iterations'.")
#
def locked?
if authentication_authority
- !!(authentication_authority =~ /DisabledUser/ )
+ !!(authentication_authority.include?("DisabledUser"))
else
false
end
diff --git a/lib/chef/provider/user/mac.rb b/lib/chef/provider/user/mac.rb
index 5604244f7f..94b0ce0b21 100644
--- a/lib/chef/provider/user/mac.rb
+++ b/lib/chef/provider/user/mac.rb
@@ -29,13 +29,12 @@ class Chef
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.
+ # of the mac_user resource
class MacUser < Chef::Provider::User
include Chef::Mixin::Which
provides :mac_user
- provides :user, os: "darwin", platform_version: ">= 10.14"
+ provides :user, os: "darwin"
attr_reader :user_plist, :admin_group_plist
@@ -394,23 +393,21 @@ class Chef
# 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
+ @user_group_info ||= 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
def secure_token_enabled?
diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb
index 9e21e5a816..924098eea0 100644
--- a/lib/chef/provider/user/pw.rb
+++ b/lib/chef/provider/user/pw.rb
@@ -97,7 +97,7 @@ class Chef
command = "pw usermod #{new_resource.username} -H 0"
shell_out!(command, input: new_resource.password.to_s)
else
- logger.trace("#{new_resource} no change needed to password")
+ logger.debug("#{new_resource} no change needed to password")
end
end
end
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index 32b2c35264..8cf4ea475b 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -62,7 +62,7 @@ class Chef
# <false>:: If the users are identical
def compare_user
@change_desc = []
- unless @net_user.validate_credentials(new_resource.password)
+ if new_resource.password && !@net_user.validate_credentials(new_resource.password)
@change_desc << "update password"
end
diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb
index a93319a35a..ee986c6d59 100644
--- a/lib/chef/provider/windows_script.rb
+++ b/lib/chef/provider/windows_script.rb
@@ -83,7 +83,7 @@ class Chef
username = new_resource.user
if new_resource.domain
- username = new_resource.domain + '\\' + new_resource.user
+ username = new_resource.domain + "\\" + new_resource.user
end
# Create an ACE that allows the alternate user read access to the script
diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb
index 956d5ae64c..b0dfe20f1b 100644
--- a/lib/chef/provider/yum_repository.rb
+++ b/lib/chef/provider/yum_repository.rb
@@ -33,7 +33,7 @@ class Chef
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
@@ -81,7 +81,7 @@ class Chef
only_if "yum repolist all | grep -P '^#{new_resource.repositoryid}([ \t]|$)'"
end
- declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+ 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
diff --git a/lib/chef/provider/zypper_repository.rb b/lib/chef/provider/zypper_repository.rb
index 53dae74948..c1c863e20e 100644
--- a/lib/chef/provider/zypper_repository.rb
+++ b/lib/chef/provider/zypper_repository.rb
@@ -31,12 +31,12 @@ class Chef
action :create do
if new_resource.gpgautoimportkeys
- install_gpg_key(new_resource.gpgkey)
+ install_gpg_keys(new_resource.gpgkey)
else
- logger.trace("'gpgautoimportkeys' property is set to false. Skipping key import.")
+ logger.debug("'gpgautoimportkeys' property is set to false. Skipping key import.")
end
- declare_resource(:template, "/etc/zypp/repos.d/#{escaped_repo_name}.repo") do
+ template "/etc/zypp/repos.d/#{escaped_repo_name}.repo" do
if template_available?(new_resource.source)
source new_resource.source
else
@@ -51,13 +51,13 @@ class Chef
end
action :delete do
- declare_resource(:execute, "zypper --quiet --non-interactive removerepo #{escaped_repo_name}") do
+ 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
+ execute "zypper --quiet --non-interactive refresh --force #{escaped_repo_name}" do
only_if "zypper --quiet lr #{escaped_repo_name}"
end
end
@@ -68,15 +68,7 @@ class Chef
# 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
+ @escaped_repo_name ||= Shellwords.escape(new_resource.repo_name)
end
# determine if a template file is available in the current run
@@ -84,7 +76,7 @@ class Chef
#
# @return [Boolean] template file exists or doesn't
def template_available?(path)
- !path.nil? && run_context.has_template_in_cookbook?(cookbook_name, path)
+ !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook, path)
end
# determine if a cookbook file is available in the run
@@ -92,7 +84,7 @@ class Chef
#
# @return [Boolean] cookbook file exists or doesn't
def has_cookbook_file?(fn)
- run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
+ run_context.has_cookbook_file_in_cookbook?(new_resource.cookbook, fn)
end
# Given the provided key URI determine what kind of chef resource we need
@@ -158,27 +150,31 @@ class Chef
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.")
+ # install the provided gpg keys
+ # @param [String] uris the uri of the local or remote gpg key
+ def install_gpg_keys(uris)
+ if uris.empty?
+ logger.debug("'gpgkey' property not provided or set. Skipping gpg key import.")
return
end
- cached_keyfile = ::File.join(Chef::Config[:file_cache_path], uri.split("/")[-1])
+ # fetch each key to the cache dir either from the cookbook or by downloading it
+ # and then import the key
+ uris.each do |uri|
+ 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(key_type(uri), 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
+ execute "import gpg key from #{uri}" do
+ command "/bin/rpm --import #{cached_keyfile}"
+ not_if { key_installed?(cached_keyfile) }
+ action :run
+ end
end
end
end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 7652d60896..331f224855 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -94,7 +94,6 @@ require_relative "provider/service/aixinit"
require_relative "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"
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index e572f0667d..2442b9a050 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -42,6 +42,7 @@ require_relative "mixin/deprecation"
require_relative "mixin/properties"
require_relative "mixin/provides"
require_relative "dsl/universal"
+require_relative "constants"
class Chef
class Resource
@@ -1062,6 +1063,7 @@ class Chef
# action for the resource.
#
# @param name [Symbol] The action name to define.
+ # @param description [String] optional description for the action
# @param recipe_block The recipe to run when the action is taken. This block
# takes no parameters, and will be evaluated in a new context containing:
#
@@ -1071,14 +1073,37 @@ class Chef
#
# @return The Action class implementing the action
#
- def self.action(action, &recipe_block)
+ def self.action(action, description: nil, &recipe_block)
action = action.to_sym
declare_action_class
action_class.action(action, &recipe_block)
self.allowed_actions += [ action ]
+ # Accept any non-nil description, which will correctly override
+ # any specific inherited description.
+ action_descriptions[action] = description unless description.nil?
default_action action if Array(default_action) == [:nothing]
end
+ # Retrieve the description for a resource's action, if
+ # any description has been included in the definition.
+ #
+ # @param action [Symbol,String] the action name
+ # @return the description of the action provided, or nil if no description
+ # was defined
+ def self.action_description(action)
+ action_descriptions[action.to_sym]
+ end
+
+ # @api private
+ #
+ # @return existing action description hash, or newly-initialized
+ # hash containing action descriptions inherited from parent Resource,
+ # if any.
+ def self.action_descriptions
+ @action_descriptions ||=
+ superclass.respond_to?(:action_descriptions) ? superclass.action_descriptions.dup : { nothing: nil }
+ end
+
# Define a method to load up this resource's properties with the current
# actual values.
#
@@ -1196,9 +1221,9 @@ class Chef
#
# 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
+ 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 @action_descriptions}.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
+ 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 @action_descriptions}.freeze
include Chef::Mixin::ConvertToClassName
extend Chef::Mixin::ConvertToClassName
@@ -1338,8 +1363,9 @@ class Chef
#
# @param arg [String] version constraint to match against (e.g. "> 14")
#
- def self.chef_version_for_provides(constraint)
- @chef_version_for_provides = constraint
+ def self.chef_version_for_provides(constraint = NOT_PASSED)
+ @chef_version_for_provides = constraint unless constraint == NOT_PASSED
+ @chef_version_for_provides ||= nil
end
# Mark this resource as providing particular DSL.
@@ -1352,14 +1378,11 @@ class Chef
def self.provides(name, **options, &block)
name = name.to_sym
- # 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
+ 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)
diff --git a/lib/chef/resource/alternatives.rb b/lib/chef/resource/alternatives.rb
index fe5af6b7b6..59939543ab 100644
--- a/lib/chef/resource/alternatives.rb
+++ b/lib/chef/resource/alternatives.rb
@@ -26,7 +26,7 @@ class Chef
provides(:alternatives) { true }
- description "The alternatives resource allows for configuration of command alternatives in Linux using the alternatives or update-alternatives packages."
+ description "Use the **alternatives** resource to configure command alternatives in Linux using the alternatives or update-alternatives packages."
introduced "16.0"
examples <<~DOC
**Install an alternative**:
@@ -122,7 +122,7 @@ class Chef
end
end
- action :install do
+ action :install, description: "Install an alternative on the system including symlinks." 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)
@@ -133,7 +133,7 @@ class Chef
end
end
- action :set do
+ action :set, description: "Set the symlink for an alternative." 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)
@@ -144,7 +144,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove an alternative and all associated links." 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)
@@ -152,13 +152,13 @@ class Chef
end
end
- action :auto do
+ action :auto, description: "Set an alternative up in automatic mode with the highest priority automatically selected." 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
+ action :refresh, description: "Refresh alternatives." do
converge_by("refreshing alternative #{new_resource.link_name}") do
shell_out(alternatives_cmd, "--refresh", new_resource.link_name)
end
diff --git a/lib/chef/resource/apt_package.rb b/lib/chef/resource/apt_package.rb
index 0a31f89af3..331d411373 100644
--- a/lib/chef/resource/apt_package.rb
+++ b/lib/chef/resource/apt_package.rb
@@ -72,7 +72,7 @@ class Chef
property :response_file_variables, Hash,
description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.",
- default: lazy { {} }, desired_state: false
+ default: {}, desired_state: false
end
end
diff --git a/lib/chef/resource/apt_preference.rb b/lib/chef/resource/apt_preference.rb
index fd987466ea..09e56b1edf 100644
--- a/lib/chef/resource/apt_preference.rb
+++ b/lib/chef/resource/apt_preference.rb
@@ -91,7 +91,7 @@ class Chef
end
end
- action :add do
+ action :add, description: "Creates a preferences file under `/etc/apt/preferences.d`." do
return unless debian?
preference = build_pref(
@@ -130,7 +130,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Removes the preferences file, thus unpinning the package." do
return unless debian?
sanitized_prefname = safe_name(new_resource.package_name)
diff --git a/lib/chef/resource/apt_repository.rb b/lib/chef/resource/apt_repository.rb
index da8ca78413..97dc26b89e 100644
--- a/lib/chef/resource/apt_repository.rb
+++ b/lib/chef/resource/apt_repository.rb
@@ -36,7 +36,7 @@ class Chef
examples <<~DOC
**Add repository with basic settings**:
- ```ruby
+ ```ruby
apt_repository 'nginx' do
uri 'http://nginx.org/packages/ubuntu/'
components ['nginx']
@@ -128,7 +128,7 @@ class Chef
property :components, Array,
description: "Package groupings, such as 'main' and 'stable'.",
- default: lazy { [] }, default_description: "`main` if using a PPA repository."
+ default: [], 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`."
@@ -147,7 +147,7 @@ class Chef
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 }
+ default: [], 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=`."
@@ -409,7 +409,7 @@ class Chef
end
end
- action :add do
+ action :add, description: "Creates a repository file at `/etc/apt/sources.list.d/` and builds the repository listing." do
return unless debian?
execute "apt-cache gencaches" do
@@ -459,7 +459,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Removes the repository listing." do
return unless debian?
cleanup_legacy_file!
@@ -477,7 +477,7 @@ class Chef
end
end
else
- logger.trace("/etc/apt/sources.list.d/#{new_resource.repo_name}.list does not exist. Nothing to do")
+ logger.debug("/etc/apt/sources.list.d/#{new_resource.repo_name}.list does not exist. Nothing to do")
end
end
diff --git a/lib/chef/resource/apt_update.rb b/lib/chef/resource/apt_update.rb
index e5f75143bb..cfecef02cf 100644
--- a/lib/chef/resource/apt_update.rb
+++ b/lib/chef/resource/apt_update.rb
@@ -17,6 +17,7 @@
#
require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
@@ -32,8 +33,8 @@ class Chef
```ruby
apt_update 'all platforms' do
- frequency 86400
- action :periodic
+ frequency 86400
+ action :periodic
end
```
@@ -85,7 +86,7 @@ class Chef
end
end
- action :periodic do
+ action :periodic, description: "Update the Apt repository at the interval specified by the `frequency` property." do
return unless debian?
unless apt_up_to_date?
@@ -95,7 +96,7 @@ class Chef
end
end
- action :update do
+ action :update, description: "Update the Apt repository at the start of a #{ChefUtils::Dist::Infra::PRODUCT} run." do
return unless debian?
converge_by "force update new lists of packages" do
diff --git a/lib/chef/resource/archive_file.rb b/lib/chef/resource/archive_file.rb
index 4d77ee979b..212835114b 100644
--- a/lib/chef/resource/archive_file.rb
+++ b/lib/chef/resource/archive_file.rb
@@ -67,7 +67,7 @@ class Chef
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"
+ default: "755", default_description: "'755'"
property :destination, String,
description: "The file path to extract the archive file to.",
@@ -85,8 +85,7 @@ class Chef
alias_method :extract_options, :options
alias_method :extract_to, :destination
- action :extract do
- description "Extract and archive file."
+ action :extract, description: "Extract and archive file." do
require_libarchive
diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb
index 50e2d06391..360c064ff0 100644
--- a/lib/chef/resource/breakpoint.rb
+++ b/lib/chef/resource/breakpoint.rb
@@ -92,7 +92,7 @@ class Chef
super(caller.first, *args)
end
- action :break do
+ action :break, description: "Add a breakpoint for use with #{ChefUtils::Dist::Infra::SHELL}" do
if defined?(Shell) && Shell.running?
with_run_context :parent do
run_context.resource_collection.iterator.pause
diff --git a/lib/chef/resource/build_essential.rb b/lib/chef/resource/build_essential.rb
index 3039f709c8..8b4c142e0f 100644
--- a/lib/chef/resource/build_essential.rb
+++ b/lib/chef/resource/build_essential.rb
@@ -57,10 +57,7 @@ class Chef
introduced: "15.5",
default: false, desired_state: false # FIXME: make this default to true
- action :install do
-
- description "Install build essential packages"
-
+ action :install, description: "Install build essential packages." do
case
when debian?
package %w{ autoconf binutils-doc bison build-essential flex gettext ncurses-dev }
@@ -122,8 +119,7 @@ class Chef
end
end
- action :upgrade do
- description "Upgrade build essential (Xcode Command Line) tools on macOS"
+ action :upgrade, description: "Upgrade the Xcode CLI Tools on macOS hosts. **New in Chef Infra Client 16**" do
if macos?
pkg_label = xcode_cli_package_label
diff --git a/lib/chef/resource/chef_client_config.rb b/lib/chef/resource/chef_client_config.rb
index deee413ba4..770aedf442 100644
--- a/lib/chef/resource/chef_client_config.rb
+++ b/lib/chef/resource/chef_client_config.rb
@@ -110,7 +110,7 @@ class Chef
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"
+ 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."
@@ -139,7 +139,7 @@ class Chef
DESC
property :formatters, Array,
- description: "",
+ description: "Client logging formatters to load.",
default: []
property :event_loggers, Array,
@@ -227,7 +227,7 @@ class Chef
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
+ action :create, description: "Create a client.rb config file for configuring #{ChefUtils::Dist::Infra::PRODUCT}." do
unless ::Dir.exist?(new_resource.config_directory)
directory new_resource.config_directory do
user new_resource.user unless new_resource.user.nil?
@@ -284,7 +284,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a client.rb config file for configuring #{ChefUtils::Dist::Infra::PRODUCT}." do
file ::File.join(new_resource.config_directory, "client.rb") do
action :delete
end
diff --git a/lib/chef/resource/chef_client_cron.rb b/lib/chef/resource/chef_client_cron.rb
index 003b28d7b3..cab2adacf2 100644
--- a/lib/chef/resource/chef_client_cron.rb
+++ b/lib/chef/resource/chef_client_cron.rb
@@ -131,11 +131,11 @@ class Chef
description: "The path to the #{ChefUtils::Dist::Infra::CLIENT} binary."
property :daemon_options, Array,
- default: lazy { [] },
+ default: [],
description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command."
property :environment, Hash,
- default: lazy { {} },
+ default: {},
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],
@@ -144,7 +144,7 @@ class Chef
coerce: proc { |x| Integer(x) },
callbacks: { "should be an Integer between -20 and 19" => proc { |v| v >= -20 && v <= 19 } }
- action :add do
+ action :add, description: "Add a cron job to run #{ChefUtils::Dist::Infra::PRODUCT}." 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
@@ -168,7 +168,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a cron job for #{ChefUtils::Dist::Infra::PRODUCT}." do
declare_resource(cron_resource_type, new_resource.job_name) do
action :delete
end
@@ -213,7 +213,7 @@ class Chef
#
def log_command
if new_resource.append_log_file
- "-L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}"
+ ">> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1"
else
"> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1"
end
diff --git a/lib/chef/resource/chef_client_launchd.rb b/lib/chef/resource/chef_client_launchd.rb
index 0e173050d0..86c2b6ce7d 100644
--- a/lib/chef/resource/chef_client_launchd.rb
+++ b/lib/chef/resource/chef_client_launchd.rb
@@ -23,7 +23,7 @@ class Chef
provides :chef_client_launchd
- description "Use the **chef_client_launchd** resource to configure the #{ChefUtils::Dist::Infra::PRODUCT} to run on a schedule."
+ description "Use the **chef_client_launchd** resource to configure the #{ChefUtils::Dist::Infra::PRODUCT} to run on a schedule on macOS systems."
introduced "16.5"
examples <<~DOC
**Set the #{ChefUtils::Dist::Infra::PRODUCT} to run on a schedule**:
@@ -86,11 +86,11 @@ class Chef
property :daemon_options, Array,
description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
- default: lazy { [] }
+ default: []
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 { {} }
+ default: {}
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.",
@@ -101,7 +101,7 @@ class Chef
description: "Run the #{ChefUtils::Dist::Infra::CLIENT} process with low priority disk IO",
default: true
- action :enable do
+ action :enable, description: "Enable running #{ChefUtils::Dist::Infra::PRODUCT} on a schedule using launchd." do
unless ::Dir.exist?(new_resource.log_directory)
directory new_resource.log_directory do
owner new_resource.user
@@ -134,7 +134,7 @@ class Chef
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"]
+ "echo; echo #{ChefUtils::Dist::Infra::PRODUCT} launchd daemon config has been updated. Manually unloading and reloading the daemon; echo Now unloading the daemon; /bin/launchctl unload /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist; sleep 2; echo Now loading the daemon; /bin/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
@@ -148,7 +148,7 @@ class Chef
end
end
- action :disable do
+ action :disable, description: "Disable running #{ChefUtils::Dist::Infra::PRODUCT} on a schedule using launchd" do
service ChefUtils::Dist::Infra::PRODUCT do
service_name "com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}"
action :disable
diff --git a/lib/chef/resource/chef_client_scheduled_task.rb b/lib/chef/resource/chef_client_scheduled_task.rb
index 25780afdf4..e963ac21cc 100644
--- a/lib/chef/resource/chef_client_scheduled_task.rb
+++ b/lib/chef/resource/chef_client_scheduled_task.rb
@@ -30,33 +30,33 @@ class Chef
**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'
+ 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
+ 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
+ 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
+ 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
@@ -127,9 +127,9 @@ class Chef
property :daemon_options, Array,
description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
- default: lazy { [] }
+ default: []
- action :add do
+ action :add, description: "Add a Windows Scheduled Task that runs #{ChefUtils::Dist::Infra::PRODUCT}." 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
@@ -157,7 +157,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a Windows Scheduled Task that runs #{ChefUtils::Dist::Infra::PRODUCT}." do
windows_task new_resource.task_name do
action :delete
end
diff --git a/lib/chef/resource/chef_client_systemd_timer.rb b/lib/chef/resource/chef_client_systemd_timer.rb
index e911fc2cb0..57dc8a36a4 100644
--- a/lib/chef/resource/chef_client_systemd_timer.rb
+++ b/lib/chef/resource/chef_client_systemd_timer.rb
@@ -92,11 +92,11 @@ class Chef
property :daemon_options, Array,
description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
- default: lazy { [] }
+ default: []
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 { {} }
+ default: {}
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.",
@@ -104,7 +104,7 @@ class Chef
coerce: proc { |x| Integer(x) },
callbacks: { "should be a positive Integer" => proc { |v| v > 0 } }
- action :add do
+ action :add, description: "Add a systemd timer that runs #{ChefUtils::Dist::Infra::PRODUCT}." do
systemd_unit "#{new_resource.job_name}.service" do
content service_content
action :create
@@ -116,7 +116,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a systemd timer that runs #{ChefUtils::Dist::Infra::PRODUCT}." do
systemd_unit "#{new_resource.job_name}.service" do
action :delete
end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index fc23555cbd..2c5b342bce 100644
--- a/lib/chef/resource/chef_gem.rb
+++ b/lib/chef/resource/chef_gem.rb
@@ -49,7 +49,7 @@ class Chef
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 'right_aws' do
+ chef_gem 'loofah' do
compile_time false
action :install
end
@@ -57,7 +57,7 @@ class Chef
To install a gem while the resource collection is being built (the compile phase), set the `compile_time` property to `true`:
```ruby
- chef_gem 'right_aws' do
+ chef_gem 'loofah' do
compile_time true
action :install
end
diff --git a/lib/chef/resource/chef_handler.rb b/lib/chef/resource/chef_handler.rb
index a006b2648a..099f95a52e 100644
--- a/lib/chef/resource/chef_handler.rb
+++ b/lib/chef/resource/chef_handler.rb
@@ -182,7 +182,7 @@ class Chef
property :arguments, [Array, Hash],
description: "Arguments to pass the handler's class initializer.",
- default: lazy { [] }
+ default: []
property :type, Hash,
description: "The type of handler to register as, i.e. :report, :exception or both.",
@@ -194,9 +194,7 @@ class Chef
# 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"
-
+ action :enable, description: "Enables the handler for the current #{ChefUtils::Dist::Infra::PRODUCT} run on the current node." do
class_name = new_resource.class_name
new_resource.type.each do |type, enable|
next unless enable
@@ -218,9 +216,7 @@ class Chef
end
end
- action :disable do
- description "Disables the handler for the current #{ChefUtils::Dist::Infra::PRODUCT} run on the current node"
-
+ action :disable, description: "Disables the handler for the current #{ChefUtils::Dist::Infra::PRODUCT} run on the current node." do
new_resource.type.each_key do |type|
unregister_handler(type, new_resource.class_name)
end
diff --git a/lib/chef/resource/chef_sleep.rb b/lib/chef/resource/chef_sleep.rb
index c6d3e45877..13f737c0c4 100644
--- a/lib/chef/resource/chef_sleep.rb
+++ b/lib/chef/resource/chef_sleep.rb
@@ -62,7 +62,7 @@ class Chef
coerce: proc { |s| Integer(s) },
name_property: true
- action :sleep do
+ action :sleep, description: "Pause the #{ChefUtils::Dist::Infra::PRODUCT} run for a specified number of seconds." do
converge_by("sleep #{new_resource.seconds} seconds") do
sleep(new_resource.seconds)
end
diff --git a/lib/chef/resource/chef_vault_secret.rb b/lib/chef/resource/chef_vault_secret.rb
index 1c8fa985f9..80e3f0f471 100644
--- a/lib/chef/resource/chef_vault_secret.rb
+++ b/lib/chef/resource/chef_vault_secret.rb
@@ -88,8 +88,7 @@ class Chef
end
- action :create do
- description "Creates the item, or updates it if it already exists."
+ action :create, description: "Creates the item, or updates it if it already exists." do
converge_if_changed do
item = ChefVault::Item.new(new_resource.data_bag, new_resource.id)
@@ -111,15 +110,11 @@ class Chef
end
end
- action :create_if_missing do
- description "Calls the create action unless it exists."
-
+ action :create_if_missing, description: "Calls the create action unless it exists." do
action_create if current_resource.nil?
end
- action :delete do
- description "Deletes the item and the item's keys ('id'_keys)."
-
+ action :delete, description: "Deletes the item and the item's keys ('id'_keys)." do
chef_data_bag_item new_resource.id do
data_bag new_resource.data_bag
action :delete
diff --git a/lib/chef/resource/chocolatey_config.rb b/lib/chef/resource/chocolatey_config.rb
index c4f100c28d..637d250e9b 100644
--- a/lib/chef/resource/chocolatey_config.rb
+++ b/lib/chef/resource/chocolatey_config.rb
@@ -68,9 +68,7 @@ class Chef
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."
-
+ action :set, description: "Sets a Chocolatey config value." do
raise "#{new_resource}: When adding a Chocolatey config you must pass the 'value' property!" unless new_resource.value
converge_if_changed do
@@ -78,9 +76,7 @@ class Chef
end
end
- action :unset do
- description "Unsets a Chocolatey config value."
-
+ action :unset, description: "Unsets a Chocolatey config value." do
if current_resource
converge_by("unset Chocolatey config '#{new_resource.config_key}'") do
shell_out!(choco_cmd("unset"))
diff --git a/lib/chef/resource/chocolatey_feature.rb b/lib/chef/resource/chocolatey_feature.rb
index 66752fbd5d..b56d11376e 100644
--- a/lib/chef/resource/chocolatey_feature.rb
+++ b/lib/chef/resource/chocolatey_feature.rb
@@ -65,9 +65,7 @@ class Chef
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."
-
+ action :enable, description: "Enables a named Chocolatey feature." do
if current_resource.feature_state != true
converge_by("enable Chocolatey feature '#{new_resource.feature_name}'") do
shell_out!(choco_cmd("enable"))
@@ -75,9 +73,7 @@ class Chef
end
end
- action :disable do
- description "Disables a named Chocolatey feature."
-
+ action :disable, description: "Disables a named Chocolatey feature." do
if current_resource.feature_state == true
converge_by("disable Chocolatey feature '#{new_resource.feature_name}'") do
shell_out!(choco_cmd("disable"))
diff --git a/lib/chef/resource/chocolatey_source.rb b/lib/chef/resource/chocolatey_source.rb
index cee4289682..1b287fc188 100644
--- a/lib/chef/resource/chocolatey_source.rb
+++ b/lib/chef/resource/chocolatey_source.rb
@@ -89,8 +89,7 @@ class Chef
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."
+ action :add, description: "Adds a Chocolatey source" do
raise "#{new_resource}: When adding a Chocolatey source you must pass the 'source' property!" unless new_resource.source
@@ -99,8 +98,7 @@ class Chef
end
end
- action :remove do
- description "Removes a Chocolatey source."
+ action :remove, description: "Removes a Chocolatey source." do
if current_resource
converge_by("remove Chocolatey source '#{new_resource.source_name}'") do
@@ -109,9 +107,7 @@ class Chef
end
end
- action :disable do
- description "Disables a Chocolatey source."
-
+ action :disable, description: "Disables a Chocolatey source. **New in Chef Infra Client 15.1.**" do
if current_resource.disabled != true
converge_by("disable Chocolatey source '#{new_resource.source_name}'") do
shell_out!(choco_cmd("disable"))
@@ -119,9 +115,7 @@ class Chef
end
end
- action :enable do
- description "Enables a Chocolatey source."
-
+ action :enable, description: "Enables a Chocolatey source. **New in Chef Infra Client 15.1.**" do
if current_resource.disabled == true
converge_by("enable Chocolatey source '#{new_resource.source_name}'") do
shell_out!(choco_cmd("enable"))
diff --git a/lib/chef/resource/cron/_cron_shared.rb b/lib/chef/resource/cron/_cron_shared.rb
index 6d11035862..3dba74e77f 100644
--- a/lib/chef/resource/cron/_cron_shared.rb
+++ b/lib/chef/resource/cron/_cron_shared.rb
@@ -61,7 +61,7 @@ property :user, String,
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 { {} }
+ default: {}
property :time_out, Hash,
description: "A Hash of timeouts in the form of `({'OPTION' => 'VALUE'})`. Accepted valid options are:
@@ -69,7 +69,7 @@ property :time_out, Hash,
- `foreground` (BOOL, default: 'false'),
- `kill-after` (in seconds),
- `signal` (a name like 'HUP' or a number)",
- default: lazy { {} },
+ default: {},
introduced: "15.7",
coerce: proc { |h|
if h.is_a?(Hash)
diff --git a/lib/chef/resource/cron/cron_d.rb b/lib/chef/resource/cron/cron_d.rb
index 6262ef453a..2806f55415 100644
--- a/lib/chef/resource/cron/cron_d.rb
+++ b/lib/chef/resource/cron/cron_d.rb
@@ -29,7 +29,7 @@ class Chef
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."
+ 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**
@@ -116,19 +116,17 @@ class Chef
end
action :create do
- description "Add a cron definition file to /etc/cron.d."
+ 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."
+ action :create_if_missing, description: "Add a cron definition file to `/etc/cron.d`, but do not update an existing file." do
create_template(:create_if_missing)
end
- action :delete do
- description "Remove a cron definition file from /etc/cron.d if it exists."
+ action :delete, description: "Remove a cron definition file from `/etc/cron.d` if it exists." do
# cleanup the legacy named job if it exists
file "legacy named cron.d file" do
@@ -160,6 +158,7 @@ class Chef
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,
diff --git a/lib/chef/resource/cron_access.rb b/lib/chef/resource/cron_access.rb
index 3ea777ce9c..233931e64a 100644
--- a/lib/chef/resource/cron_access.rb
+++ b/lib/chef/resource/cron_access.rb
@@ -64,8 +64,7 @@ class Chef
"default" => "/etc",
}.freeze
- action :allow do
- description "Add the user to the cron.allow file."
+ action :allow, description: "Add the user to the cron.allow file." do
allow_path = ::File.join(value_for_platform_family(CRON_PATHS), "cron.allow")
with_run_context :root do
@@ -81,8 +80,7 @@ class Chef
end
end
- action :deny do
- description "Add the user to the cron.deny file."
+ action :deny, description: "Add the user to the cron.deny file." do
deny_path = ::File.join(value_for_platform_family(CRON_PATHS), "cron.deny")
with_run_context :root do
diff --git a/lib/chef/resource/dmg_package.rb b/lib/chef/resource/dmg_package.rb
index c6cd04156c..9f898e92ea 100644
--- a/lib/chef/resource/dmg_package.rb
+++ b/lib/chef/resource/dmg_package.rb
@@ -121,9 +121,7 @@ class Chef
end
end
- action :install do
- description "Installs the application."
-
+ action :install, description: "Installs the application." do
if current_resource.nil?
if new_resource.source
remote_file dmg_file do
@@ -171,13 +169,11 @@ class Chef
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
+ @dmg_file ||= if new_resource.file.nil?
+ "#{Chef::Config[:file_cache_path]}/#{new_resource.dmg_name}.dmg"
+ else
+ new_resource.file
+ end
end
# @return [String] the hdiutil flag for handling DMGs with a password
diff --git a/lib/chef/resource/dpkg_package.rb b/lib/chef/resource/dpkg_package.rb
index 466b17d702..1ad460e591 100644
--- a/lib/chef/resource/dpkg_package.rb
+++ b/lib/chef/resource/dpkg_package.rb
@@ -36,7 +36,7 @@ class Chef
property :response_file_variables, Hash,
description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.",
- default: lazy { {} }, desired_state: false
+ default: {}, desired_state: false
end
end
end
diff --git a/lib/chef/resource/dsc_resource.rb b/lib/chef/resource/dsc_resource.rb
index 679deef47b..a869e2acc4 100644
--- a/lib/chef/resource/dsc_resource.rb
+++ b/lib/chef/resource/dsc_resource.rb
@@ -74,7 +74,7 @@ class Chef
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."
+ 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` property."
def property(property_name, value = nil)
unless property_name.is_a?(Symbol)
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 5a78160642..1f7e1b2c1b 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -304,9 +304,9 @@ class Chef
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**:
+ **Use the search Infra Language helper to find users**:
- The following example shows how to use the `search` method in the Recipe DSL to
+ The following example shows how to use the `search` method in the Chef Infra Language to
search for users:
```ruby
@@ -515,7 +515,7 @@ class Chef
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."
+ description: "An optional property to set the command to be executed if it differs from the resource block's name. Note: Use the **execute** resource to run a single command. Use multiple **execute** resource blocks to run multiple commands."
property :umask, [ String, Integer ],
description: "The file mode creation mask, or umask."
@@ -572,6 +572,10 @@ class Chef
introduced: "16.2",
description: "An optional property to set the input sent to the command as STDIN."
+ property :login, [ TrueClass, FalseClass ], default: false,
+ introduced: "17.0",
+ description: "Use a login shell to run the commands instead of inheriting the existing execution environment."
+
alias :env :environment
def self.set_guard_inherited_attributes(*inherited_attributes)
@@ -629,13 +633,13 @@ class Chef
end
# if domain is provided in both username and domain
- if specified_user.is_a?(String) && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_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('\\')
+ domain_and_user = user.split("\\")
if domain_and_user.length == 2
domain = domain_and_user[0]
@@ -666,7 +670,8 @@ class Chef
:group,
:password,
:user,
- :umask
+ :umask,
+ :login
)
end
diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb
index 214f8018ab..05cb4658d5 100644
--- a/lib/chef/resource/file.rb
+++ b/lib/chef/resource/file.rb
@@ -32,7 +32,7 @@ class Chef
provides :file
- description "Use the **file** resource to manage files directly on a node."
+ 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
@@ -81,7 +81,7 @@ class Chef
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 { [] }
+ property :verifications, Array, default: lazy { [] }, desired_state: false, skip_docs: true
def verify(command = nil, opts = {}, &block)
unless command.nil? || [String, Symbol].include?(command.class)
diff --git a/lib/chef/resource/gem_package.rb b/lib/chef/resource/gem_package.rb
index c893e7a2f9..a3ad5f614b 100644
--- a/lib/chef/resource/gem_package.rb
+++ b/lib/chef/resource/gem_package.rb
@@ -44,8 +44,8 @@ class Chef
**Install a gem file from the local file system**
```ruby
- gem_package 'right_aws' do
- source '/tmp/right_aws-1.11.0.gem'
+ gem_package 'loofah' do
+ source '/tmp/loofah-2.7.0.gem'
action :install
end
```
diff --git a/lib/chef/resource/group.rb b/lib/chef/resource/group.rb
index 3a129592d0..545cd0b35d 100644
--- a/lib/chef/resource/group.rb
+++ b/lib/chef/resource/group.rb
@@ -37,22 +37,22 @@ class Chef
property :gid, [ String, Integer ],
description: "The identifier for the group."
- property :members, [String, Array], default: lazy { [] },
+ property :members, [String, Array], default: [],
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']."
- property :excluded_members, [String, Array], default: lazy { [] },
+ property :excluded_members, [String, Array], default: [],
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."
+ description: "Remove users from a group. May only be used when `append` is set to `true`."
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."
+ 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."
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."
+ description: "Set to `true` if the group belongs to a system group."
property :non_unique, [ TrueClass, FalseClass ], default: false,
- description: "Allow gid duplication. May only be used with the Groupadd provider."
+ description: "Allow gid duplication. May only be used with the `Groupadd` user resource provider."
property :comment, String,
introduced: "14.9",
diff --git a/lib/chef/resource/homebrew_cask.rb b/lib/chef/resource/homebrew_cask.rb
index 3b0974348c..7a862e8638 100644
--- a/lib/chef/resource/homebrew_cask.rb
+++ b/lib/chef/resource/homebrew_cask.rb
@@ -51,16 +51,20 @@ class Chef
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
+ default: lazy { find_homebrew_username },
+ default_description: "Calculated default username"\
+
+ action :install, description: "Install an application that is packaged as a Homebrew cask." do
+ if new_resource.install_cask
+ homebrew_tap "homebrew/cask" do
+ homebrew_path new_resource.homebrew_path
+ owner new_resource.owner
+ end
+ end
unless casked?
converge_by("install cask #{new_resource.cask_name} #{new_resource.options}") do
- shell_out!("#{new_resource.homebrew_path} cask install #{new_resource.cask_name} #{new_resource.options}",
+ 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))
@@ -68,14 +72,17 @@ class Chef
end
end
- action :remove do
- description "Remove an application packaged as a Homebrew cask."
-
- homebrew_tap "homebrew/cask" if new_resource.install_cask
+ action :remove, description: "Remove an application that is packaged as a Homebrew cask." do
+ if new_resource.install_cask
+ homebrew_tap "homebrew/cask" do
+ homebrew_path new_resource.homebrew_path
+ owner new_resource.owner
+ end
+ end
if casked?
converge_by("uninstall cask #{new_resource.cask_name}") do
- shell_out!("#{new_resource.homebrew_path} cask uninstall #{new_resource.cask_name}",
+ 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))
@@ -93,7 +100,7 @@ class Chef
# @return [Boolean]
def casked?
unscoped_name = new_resource.cask_name.split("/").last
- shell_out!("#{new_resource.homebrew_path} cask list 2>/dev/null",
+ 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)
diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb
index 3874622005..040eff9f59 100644
--- a/lib/chef/resource/homebrew_package.rb
+++ b/lib/chef/resource/homebrew_package.rb
@@ -62,7 +62,7 @@ class Chef
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."
+ description: "The name or uid of the Homebrew owner to be used by #{ChefUtils::Dist::Infra::PRODUCT} when executing a command.\n\n#{ChefUtils::Dist::Infra::PRODUCT}, by default, will attempt to execute a Homebrew command as the owner of the `/usr/local/bin/brew` executable. If that executable does not exist, #{ChefUtils::Dist::Infra::PRODUCT} will attempt to find the user by executing `which brew`. If that executable cannot be found, #{ChefUtils::Dist::Infra::PRODUCT} will print an error message: `Could not find the 'brew' executable in /usr/local/bin or anywhere on the path.`.\n\nSet this property to specify the Homebrew owner for situations where Chef Infra Client cannot automatically detect the correct owner.'"
end
end
diff --git a/lib/chef/resource/homebrew_tap.rb b/lib/chef/resource/homebrew_tap.rb
index 937a9ab420..05567c5166 100644
--- a/lib/chef/resource/homebrew_tap.rb
+++ b/lib/chef/resource/homebrew_tap.rb
@@ -51,11 +51,10 @@ class Chef
property :owner, String,
description: "The owner of the Homebrew installation.",
- default: lazy { find_homebrew_username }
-
- action :tap do
- description "Add a Homebrew tap."
+ default: lazy { find_homebrew_username },
+ default_description: "Calculated default username"
+ action :tap, description: "Add a Homebrew tap." do
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 || ""}",
@@ -66,9 +65,7 @@ class Chef
end
end
- action :untap do
- description "Remove a Homebrew tap."
-
+ action :untap, description: "Remove a Homebrew tap." do
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}",
diff --git a/lib/chef/resource/homebrew_update.rb b/lib/chef/resource/homebrew_update.rb
index 27b352bfb6..ee353f1fe5 100644
--- a/lib/chef/resource/homebrew_update.rb
+++ b/lib/chef/resource/homebrew_update.rb
@@ -88,7 +88,7 @@ class Chef
end
end
- action :periodic do
+ action :periodic, description: "Run a periodic update based on the frequency property." do
return unless macos?
unless brew_up_to_date?
@@ -98,7 +98,7 @@ class Chef
end
end
- action :update do
+ action :update, description: "Run an immediate update." do
return unless macos?
converge_by "force update new lists of packages" do
diff --git a/lib/chef/resource/hostname.rb b/lib/chef/resource/hostname.rb
index 27903e5807..ab4ab73961 100644
--- a/lib/chef/resource/hostname.rb
+++ b/lib/chef/resource/hostname.rb
@@ -44,12 +44,34 @@ class Chef
ipaddress '198.51.100.2'
end
```
+
+ **Change the hostname of a Windows, Non-Domain joined node**:
+
+ ```ruby
+ hostname 'renaming a workgroup computer' do
+ hostname 'Foo'
+ end
+ ```
+
+ **Change the hostname of a Windows, Domain-joined node (new in 17.2)**:
+
+ ```ruby
+ hostname 'renaming a domain-joined computer' do
+ hostname 'Foo'
+ domain_user "Domain\\Someone"
+ domain_password 'SomePassword'
+ 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 :fqdn, String,
+ description: "An optional property to set the fqdn if it differs from the resource block's hostname.",
+ introduced: "17.0"
+
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."
@@ -67,6 +89,15 @@ class Chef
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
+ property :domain_user, String,
+ description: "A domain account specified in the form of DOMAIN\\user used when renaming a domain-joined device",
+ introduced: "17.2"
+
+ property :domain_password, String,
+ description: "The password to accompany the domain_user parameter",
+ sensitive: true,
+ introduced: "17.2"
+
action_class do
def append_replacing_matching_lines(path, regex, string)
text = IO.read(path).split("\n")
@@ -99,9 +130,11 @@ class Chef
end
end
- action :set do
- description "Sets the node's hostname."
+ def is_domain_joined?
+ powershell_exec!("(Get-CIMInstance -Class Win32_ComputerSystem).PartofDomain").result
+ end
+ action :set, description: "Sets the node's hostname." do
if !windows?
ohai "reload hostname" do
plugin "hostname"
@@ -109,7 +142,7 @@ class Chef
end
# set the hostname via /bin/hostname
- declare_resource(:execute, "set hostname to #{new_resource.hostname}") do
+ 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]"
@@ -117,7 +150,9 @@ class Chef
# make sure node['fqdn'] resolves via /etc/hosts
unless new_resource.ipaddress.nil?
- newline = "#{new_resource.ipaddress} #{new_resource.hostname}"
+ newline = "#{new_resource.ipaddress}"
+ newline << " #{new_resource.fqdn}" unless new_resource.fqdn.to_s.empty?
+ newline << " #{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)
@@ -127,22 +162,22 @@ class Chef
# setup the hostname to persist on a reboot
case
- when ::File.exist?("/usr/sbin/scutil")
+ when darwin?
# darwin
- declare_resource(:execute, "set HostName via scutil") do
+ 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 }
+ not_if { shell_out("/usr/sbin/scutil --get HostName").stdout.chomp == new_resource.hostname }
notifies :reload, "ohai[reload hostname]"
end
- declare_resource(:execute, "set ComputerName via scutil") do
+ 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 }
+ not_if { shell_out("/usr/sbin/scutil --get ComputerName").stdout.chomp == new_resource.hostname }
notifies :reload, "ohai[reload hostname]"
end
shortname = new_resource.hostname[/[^\.]*/]
- declare_resource(:execute, "set LocalHostName via scutil") do
+ 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 }
+ not_if { shell_out("/usr/sbin/scutil --get LocalHostName").stdout.chomp == shortname }
notifies :reload, "ohai[reload hostname]"
end
when linux?
@@ -150,7 +185,7 @@ class Chef
when ::File.exist?("/usr/bin/hostnamectl") && !docker?
# use hostnamectl whenever we find it on linux (as systemd takes over the world)
# this must come before other methods like /etc/hostname and /etc/sysconfig/network
- declare_resource(:execute, "hostnamectl set-hostname #{new_resource.hostname}") do
+ 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
@@ -160,7 +195,7 @@ class Chef
# the "platform: iox_xr, platform_family: wrlinux, os: linux" platform also hits this
# the "platform: nexus, platform_family: wrlinux, os: linux" platform also hits this
# this is also fallback for any linux systemd host in a docker container (where /usr/bin/hostnamectl will fail)
- declare_resource(:file, "/etc/hostname") do
+ file "/etc/hostname" do
atomic_update false if docker?
content "#{new_resource.hostname}\n"
owner "root"
@@ -172,7 +207,7 @@ class Chef
append_replacing_matching_lines("/etc/sysconfig/network", /^HOSTNAME\s*=/, "HOSTNAME=#{new_resource.hostname}")
when ::File.exist?("/etc/HOSTNAME")
# SuSE/openSUSE uses /etc/HOSTNAME
- declare_resource(:file, "/etc/HOSTNAME") do
+ file "/etc/HOSTNAME" do
content "#{new_resource.hostname}\n"
owner "root"
group node["root_group"]
@@ -180,7 +215,7 @@ class Chef
end
when ::File.exist?("/etc/conf.d/hostname")
# Gentoo
- declare_resource(:file, "/etc/conf.d/hostname") do
+ file "/etc/conf.d/hostname" do
content "hostname=\"#{new_resource.hostname}\"\n"
owner "root"
group node["root_group"]
@@ -196,23 +231,23 @@ class Chef
# *BSD systems with /etc/rc.conf + /etc/myname
append_replacing_matching_lines("/etc/rc.conf", /^\s+hostname\s+=/, "hostname=#{new_resource.hostname}")
- declare_resource(:file, "/etc/myname") do
+ 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
- declare_resource(:execute, "svccfg -s system/identity:node setprop config/nodename=\'#{new_resource.hostname}\'") do
+ 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
- declare_resource(:execute, "svcadm refresh") do
+ execute "svcadm refresh" do
command "svcadm refresh system/identity:node"
action :nothing
end
- declare_resource(:execute, "svcadm restart") do
+ execute "svcadm restart" do
command "svcadm restart system/identity:node"
action :nothing
end
@@ -239,13 +274,24 @@ class Chef
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
+ if is_domain_joined?
+ if new_resource.domain_user.nil? || new_resource.domain_password.nil?
+ raise "The `domain_user` and `domain_password` properties are required to change the hostname of a domain-connected Windows system."
+ else
+ converge_by "set hostname to #{new_resource.hostname}" do
+ powershell_exec! <<~EOH
+ $user = #{new_resource.domain_user}
+ $secure_password = #{new_resource.domain_password} | Convertto-SecureString -AsPlainText -Force
+ $Credentials = New-Object System.Management.Automation.PSCredential -Argumentlist ($user, $secure_password)
+ Rename-Computer -NewName #{new_resource.hostname} -DomainCredential $Credentials
+ EOH
+ end
+ end
+ else
+ converge_by "set hostname to #{new_resource.hostname}" do
+ powershell_exec!("Rename-Computer -NewName #{new_resource.hostname}")
+ end
end
-
# reboot because $windows
reboot "setting hostname" do
reason "#{ChefUtils::Dist::Infra::PRODUCT} updated system hostname"
diff --git a/lib/chef/resource/http_request.rb b/lib/chef/resource/http_request.rb
index de714ab4ab..8a1354e6cd 100644
--- a/lib/chef/resource/http_request.rb
+++ b/lib/chef/resource/http_request.rb
@@ -26,7 +26,7 @@ class Chef
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."
+ 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, :patch, :put, :post, :delete, :head, :options
@@ -34,7 +34,7 @@ class Chef
property :url, String, identity: true,
description: "The URL to which an HTTP request is sent."
- property :headers, Hash, default: lazy { {} },
+ property :headers, Hash, default: {},
description: "A Hash of custom headers."
def initialize(name, run_context = nil)
diff --git a/lib/chef/resource/inspec_waiver_file_entry.rb b/lib/chef/resource/inspec_waiver_file_entry.rb
new file mode 100644
index 0000000000..fb433edb28
--- /dev/null
+++ b/lib/chef/resource/inspec_waiver_file_entry.rb
@@ -0,0 +1,156 @@
+#
+# 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"
+autoload :YAML, "yaml"
+require "date"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class InspecWaiverFileEntry < Chef::Resource
+ provides :inspec_waiver_file_entry
+ unified_mode true
+
+ description "Use the **inspec_waiver_file_entry** resource to add or remove entries from an InSpec waiver file. This can be used in conjunction with the Compliance Phase."
+ introduced "17.1"
+ examples <<~DOC
+ **Add an InSpec waiver entry to a given waiver file**:
+
+ ```ruby
+ inspec_waiver_file_entry 'Add waiver entry for control' do
+ file_path 'C:\\chef\\inspec_waiver_file.yml'
+ control 'my_inspec_control_01'
+ run_test false
+ justification "The subject of this control is not managed by #{ChefUtils::Dist::Infra::PRODUCT} on the systems in policy group \#{node['policy_group']}"
+ expiration '2022-01-01'
+ action :add
+ end
+ ```
+
+ **Add an InSpec waiver entry to a given waiver file using the 'name' property to identify the control**:
+
+ ```ruby
+ inspec_waiver_file_entry 'my_inspec_control_01' do
+ justification "The subject of this control is not managed by #{ChefUtils::Dist::Infra::PRODUCT} on the systems in policy group \#{node['policy_group']}"
+ action :add
+ end
+ ```
+
+ **Remove an InSpec waiver entry to a given waiver file**:
+
+ ```ruby
+ inspec_waiver_file_entry "my_inspec_control_01" do
+ action :remove
+ end
+ ```
+ DOC
+
+ property :control, String,
+ name_property: true,
+ description: "The name of the control being added or removed to the waiver file"
+
+ property :file_path, String,
+ required: true,
+ description: "The path to the waiver file being modified",
+ default: "#{ChefConfig::Config.etc_chef_dir}/inspec_waivers.yml",
+ default_description: "`/etc/chef/inspec_waivers.yml` on Linux/Unix and `C:\\chef\\inspec_waivers.yml` on Windows"
+
+ property :expiration, String,
+ description: "The expiration date of the given waiver - provided in YYYY-MM-DD format",
+ callbacks: {
+ "Expiration date should be a valid calendar date and match the following format: YYYY-MM-DD" => proc { |e|
+ re = Regexp.new('\d{4}-\d{2}-\d{2}$').freeze
+ if re.match?(e)
+ Date.valid_date?(*e.split("-").map(&:to_i))
+ else
+ e.nil?
+ end
+ },
+ }
+
+ property :run_test, [true, false],
+ description: "If present and true, the control will run and be reported, but failures in it won’t make the overall run fail. If absent or false, the control will not be run."
+
+ property :justification, String,
+ description: "Can be any text you want and might include a reason for the waiver as well as who signed off on the waiver."
+
+ property :backup, [false, Integer],
+ 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.",
+ default: false
+
+ action :add do
+ if new_resource.justification.nil? || new_resource.justification == ""
+ raise Chef::Exceptions::ValidationFailed, "Entries in the InSpec waiver file must have a justification given, this parameter must have a value."
+ end
+
+ filename = new_resource.file_path
+ waiver_hash = load_waiver_file_to_hash(filename)
+ control_hash = {}
+ control_hash["expiration_date"] = new_resource.expiration.to_s unless new_resource.expiration.nil?
+ control_hash["run"] = new_resource.run_test unless new_resource.run_test.nil?
+ control_hash["justification"] = new_resource.justification.to_s
+
+ unless waiver_hash[new_resource.control] == control_hash
+ waiver_hash[new_resource.control] = control_hash
+ waiver_hash = waiver_hash.sort.to_h
+
+ file "Update Waiver File #{new_resource.file_path} to update waiver for control #{new_resource.control}" do
+ path new_resource.file_path
+ content ::YAML.dump(waiver_hash)
+ backup new_resource.backup
+ action :create
+ end
+ end
+ end
+
+ action :remove do
+ filename = new_resource.file_path
+ waiver_hash = load_waiver_file_to_hash(filename)
+ if waiver_hash.key?(new_resource.control)
+ waiver_hash.delete(new_resource.control)
+ waiver_hash = waiver_hash.sort.to_h
+ file "Update Waiver File #{new_resource.file_path} to remove waiver for control #{new_resource.control}" do
+ path new_resource.file_path
+ content ::YAML.dump(waiver_hash)
+ backup new_resource.backup
+ action :create
+ end
+ end
+ end
+
+ action_class do
+ def load_waiver_file_to_hash(file_name)
+ if file_name =~ %r{(/|C:\\).*(.yaml|.yml)}i
+ if ::File.exist?(file_name)
+ hash = ::YAML.load_file(file_name)
+ if hash == false || hash.nil? || hash == ""
+ {}
+ else
+ ::YAML.load_file(file_name)
+ end
+ else
+ {}
+ end
+ else
+ raise "Waiver files needs to be a YAML file which should have a .yaml or .yml extension -\"#{file_name}\" does not have an appropriate extension"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/kernel_module.rb b/lib/chef/resource/kernel_module.rb
index 485511470e..c4a36af9eb 100644
--- a/lib/chef/resource/kernel_module.rb
+++ b/lib/chef/resource/kernel_module.rb
@@ -93,9 +93,7 @@ class Chef
description: "The modprobe.d directory.",
default: "/etc/modprobe.d"
- action :install do
- description "Load kernel module, and ensure it loads on reboot."
-
+ action :install, description: "Load kernel module, and ensure it loads on reboot." do
with_run_context :root do
find_resource(:execute, "update initramfs") do
command initramfs_command
@@ -123,8 +121,7 @@ class Chef
end
end
- action :uninstall do
- description "Unload a kernel module and remove module config, so it doesn't load on reboot."
+ action :uninstall, description: "Unload a kernel module and remove module config, so it doesn't load on reboot." do
with_run_context :root do
find_resource(:execute, "update initramfs") do
command initramfs_command
@@ -149,9 +146,7 @@ class Chef
action_unload
end
- action :blacklist do
- description "Blacklist a kernel module."
-
+ action :blacklist, description: "Blacklist a kernel module." do
with_run_context :root do
find_resource(:execute, "update initramfs") do
command initramfs_command
@@ -167,9 +162,7 @@ class Chef
action_unload
end
- action :disable do
- description "Disable a kernel module."
-
+ action :disable, description: "Disable a kernel module. **New in Chef Infra Client 15.2.**" do
with_run_context :root do
find_resource(:execute, "update initramfs") do
command initramfs_command
@@ -185,9 +178,7 @@ class Chef
action_unload
end
- action :load do
- description "Load a kernel module."
-
+ action :load, description: "Load a kernel module." do
unless module_loaded?
converge_by("load kernel module #{new_resource.modname}") do
shell_out!("modprobe #{new_resource.modname}")
@@ -195,9 +186,7 @@ class Chef
end
end
- action :unload do
- description "Unload kernel module."
-
+ action :unload, description: "Unload kernel module." do
if module_loaded?
converge_by("unload kernel module #{new_resource.modname}") do
shell_out!("modprobe -r #{new_resource.modname}")
diff --git a/lib/chef/resource/locale.rb b/lib/chef/resource/locale.rb
index 5e4a63c06b..90377ef93a 100644
--- a/lib/chef/resource/locale.rb
+++ b/lib/chef/resource/locale.rb
@@ -49,7 +49,7 @@ class Chef
property :lc_env, Hash,
description: "A Hash of LC_* env variables in the form of `({ 'LC_ENV_VARIABLE' => 'VALUE' })`.",
- default: lazy { {} },
+ default: {},
coerce: proc { |h|
if h.respond_to?(:keys)
invalid_keys = h.keys - LC_VARIABLES
@@ -97,8 +97,7 @@ class Chef
powershell_exec("Get-WinSystemLocale").result["Name"]
end
- action :update do
- description "Update the system's locale."
+ action :update, description: "Update the system's locale." do
converge_if_changed do
set_system_locale
end
@@ -125,7 +124,7 @@ class Chef
# @raise [Mixlib::ShellOut::ShellCommandFailed] not a supported language or locale
#
def generate_locales
- shell_out!("locale-gen #{unavailable_locales.join(" ")}")
+ shell_out!("locale-gen #{unavailable_locales.join(" ")}", timeout: 1800)
end
# Sets the system locale for the current computer.
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 2cf891d530..1284572339 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -26,6 +26,7 @@ require_relative "../exceptions"
require_relative "../mixin/convert_to_class_name"
require_relative "../mixin/from_file"
require_relative "../mixin/params_validate" # for DelayedEvaluator
+require_relative "../version"
class Chef
class Resource
@@ -53,6 +54,10 @@ class Chef
resource_class.run_context = run_context
resource_class.class_from_file(filename)
+ if !resource_class.unified_mode && !deprecated_class(resource_class)
+ Chef.deprecated :unified_mode, "The #{resource_class.resource_name} resource in the #{cookbook_name} cookbook should declare `unified_mode true`", filename
+ end
+
# Make a useful string for the class (rather than <Class:312894723894>)
resource_class.instance_eval do
define_singleton_method(:to_s) do
@@ -117,6 +122,20 @@ class Chef
superclass.respond_to?(m) ? superclass.send(m) : default
end
+
+ # Return true if the resource has been deprecated on this version.
+ #
+ # XXX: for now we only look at chef_version_for_provides, reversing the
+ # resource node_map to determine if the resource provides anything which is
+ # wired up is difficult.
+ #
+ def deprecated_class(resource_class)
+ if resource_class.chef_version_for_provides && Chef::VERSION !~ resource_class.chef_version_for_provides
+ return true
+ end
+
+ false
+ end
end
end
end
diff --git a/lib/chef/resource/macos_userdefaults.rb b/lib/chef/resource/macos_userdefaults.rb
index a150aeb9ed..036bd96d8b 100644
--- a/lib/chef/resource/macos_userdefaults.rb
+++ b/lib/chef/resource/macos_userdefaults.rb
@@ -97,29 +97,29 @@ class Chef
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}.",
+ 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)
+ load_current_value do |new_resource|
+ Chef::Log.debug "#load_current_value: shelling out \"#{defaults_export_cmd(new_resource).join(" ")}\" to determine state"
+ state = shell_out(defaults_export_cmd(new_resource), user: new_resource.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}"
+ Chef::Log.debug "#load_current_value: #{defaults_export_cmd(new_resource).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
+ if plist_data.key?(new_resource.key)
+ key new_resource.key
else
current_value_does_not_exist!
end
- value plist_data[desired.key]
+ value plist_data[new_resource.key]
end
#
@@ -140,9 +140,7 @@ class Chef
state_cmd
end
- action :write do
- description "Write the value to the specified domain/key."
-
+ action :write, description: "Write the value to the specified domain/key." do
converge_if_changed do
cmd = defaults_modify_cmd
Chef::Log.debug("Updating defaults value by shelling out: #{cmd.join(" ")}")
@@ -151,9 +149,7 @@ class Chef
end
end
- action :delete do
- description "Delete a key from a domain."
-
+ action :delete, description: "Delete a key from a domain." do
# if it's not there there's nothing to remove
return unless current_resource
diff --git a/lib/chef/resource/mdadm.rb b/lib/chef/resource/mdadm.rb
index 7c1d066488..86dbb0c42a 100644
--- a/lib/chef/resource/mdadm.rb
+++ b/lib/chef/resource/mdadm.rb
@@ -31,15 +31,61 @@ class Chef
" 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."
+ examples <<~DOC
+ **Create and assemble a RAID 0 array**
+
+ The mdadm command can be used to create RAID arrays. For example, a RAID 0 array named /dev/md0 with 10 devices would have a command similar to the following:
+
+ ```
+ mdadm --create /dev/md0 --level=0 --raid-devices=10 /dev/s01.../dev/s10
+ ```
+
+ where /dev/s01 .. /dev/s10 represents 10 devices (01, 02, 03, and so on). This same command, when expressed as a recipe using the mdadm resource, would be similar to:
+
+ ```ruby
+ mdadm '/dev/md0' do
+ devices [ '/dev/s01', ... '/dev/s10' ]
+ level 0
+ action :create
+ end
+ ```
+
+ (again, where /dev/s01 .. /dev/s10 represents devices /dev/s01, /dev/s02, /dev/s03, and so on).
+
+ **Create and assemble a RAID 1 array**
+
+ ```ruby
+ mdadm '/dev/md0' do
+ devices [ '/dev/sda', '/dev/sdb' ]
+ level 1
+ action [ :create, :assemble ]
+ end
+ ```
+
+ **Create and assemble a RAID 5 array**
+
+ The mdadm command can be used to create RAID arrays. For example, a RAID 5 array named /dev/sd0 with 4, and a superblock type of 0.90 would be similar to:
+
+ ```ruby
+ mdadm '/dev/sd0' do
+ devices [ '/dev/s1', '/dev/s2', '/dev/s3', '/dev/s4' ]
+ level 5
+ metadata '0.90'
+ chunk 32
+ action :create
+ end
+ ```
+ DOC
+
default_action :create
allowed_actions :create, :assemble, :stop
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)."
+ 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`)."
property :devices, Array,
- default: lazy { [] },
+ default: [],
description: "The devices to be part of a RAID array."
# @todo this should get refactored away
@@ -63,7 +109,7 @@ class Chef
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)."
+ 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
@@ -78,7 +124,7 @@ class Chef
end
end
- action :create do
+ action :create, description: "Create an array with per-device superblocks. If an array already exists (but does not match), update that array to match." 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}"
@@ -92,11 +138,11 @@ class Chef
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})")
+ logger.debug("#{new_resource} raid device already exists, skipping create (#{new_resource.raid_device})")
end
end
- action :assemble do
+ action :assemble, description: "Assemble a previously created array into an active array." 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(" ")}"
@@ -105,11 +151,11 @@ class Chef
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})")
+ logger.debug("#{new_resource} raid device already exists, skipping assemble (#{new_resource.raid_device})")
end
end
- action :stop do
+ action :stop, description: "Stop an active array." do
if current_resource.exists
converge_by("stop RAID device #{new_resource.raid_device}") do
command = "yes | mdadm --stop #{new_resource.raid_device}"
@@ -118,7 +164,7 @@ class Chef
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")
+ logger.debug("#{new_resource} raid device doesn't exist (#{new_resource.raid_device}) - not stopping")
end
end
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index c23ba9bbee..e46abead13 100644
--- a/lib/chef/resource/mount.rb
+++ b/lib/chef/resource/mount.rb
@@ -34,6 +34,7 @@ class Chef
property :supports, [Array, Hash],
description: "Specify a Hash of supported mount features.",
default: lazy { { remount: false } },
+ default_description: "{ remount: false }",
coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x }
property :password, String,
@@ -45,7 +46,7 @@ class Chef
description: "The directory (or path) in which the device is to be mounted. Defaults to the name of the resource block if not provided."
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."
+ 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."
property :device_type, [String, Symbol],
description: "The type of device: :device, :label, or :uuid",
diff --git a/lib/chef/resource/ohai_hint.rb b/lib/chef/resource/ohai_hint.rb
index 88ea02c809..63ddc595c5 100644
--- a/lib/chef/resource/ohai_hint.rb
+++ b/lib/chef/resource/ohai_hint.rb
@@ -72,9 +72,7 @@ class Chef
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."
-
+ action :create, description: "Create an Ohai hint file." do
directory ::Ohai::Config.ohai.hints_path.first do
action :create
recursive true
@@ -86,9 +84,7 @@ class Chef
end
end
- action :delete do
- description "Delete an Ohai hint file."
-
+ action :delete, description: "Delete an Ohai hint file." do
file ohai_hint_file_path(new_resource.hint_name) do
action :delete
notifies :reload, ohai[reload ohai post hint removal]
diff --git a/lib/chef/resource/openbsd_package.rb b/lib/chef/resource/openbsd_package.rb
index af632ebb57..eecb188999 100644
--- a/lib/chef/resource/openbsd_package.rb
+++ b/lib/chef/resource/openbsd_package.rb
@@ -31,6 +31,23 @@ class Chef
description "Use the **openbsd_package** resource to manage packages for the OpenBSD platform."
introduced "12.1"
+ examples <<~DOC
+ **Install a package**
+
+ ```ruby
+ openbsd_package 'name of package' do
+ action :install
+ end
+ ```
+
+ **Remove a package**
+
+ ```ruby
+ openbsd_package 'name of package' do
+ action :remove
+ end
+ ```
+ DOC
property :package_name, String,
description: "An optional property to set the package name if it differs from the resource block's name.",
diff --git a/lib/chef/resource/openssl_dhparam.rb b/lib/chef/resource/openssl_dhparam.rb
index 3d20b1b439..a34c79df17 100644
--- a/lib/chef/resource/openssl_dhparam.rb
+++ b/lib/chef/resource/openssl_dhparam.rb
@@ -88,8 +88,7 @@ class Chef
description: "The permission mode applied to all files created by the resource.",
default: "0640"
- action :create do
- description "Create the dhparam file."
+ action :create, description: "Create the `dhparam.pem` file." do
dhparam_content = nil
unless dhparam_pem_valid?(new_resource.path)
dhparam_content = gen_dhparam(new_resource.key_length, new_resource.generator).to_pem
diff --git a/lib/chef/resource/openssl_ec_private_key.rb b/lib/chef/resource/openssl_ec_private_key.rb
index 7625b5ea6e..5cd5b97c83 100644
--- a/lib/chef/resource/openssl_ec_private_key.rb
+++ b/lib/chef/resource/openssl_ec_private_key.rb
@@ -31,7 +31,7 @@ class Chef
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
+ **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
@@ -41,7 +41,7 @@ class Chef
end
```
- Generate a new ec private key with prime256v1 key curve and aes-128-cbc cipher
+ **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
@@ -88,9 +88,7 @@ class Chef
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"
-
+ action :create, description: "Generate the EC private key file." do
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} "\
diff --git a/lib/chef/resource/openssl_ec_public_key.rb b/lib/chef/resource/openssl_ec_public_key.rb
index 44441eb72d..b66687f6e7 100644
--- a/lib/chef/resource/openssl_ec_public_key.rb
+++ b/lib/chef/resource/openssl_ec_public_key.rb
@@ -74,9 +74,7 @@ class Chef
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"
-
+ action :create, description: "Generate the EC public key file from a private key." do
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)
diff --git a/lib/chef/resource/openssl_rsa_private_key.rb b/lib/chef/resource/openssl_rsa_private_key.rb
index e9e6ef24ca..5f0f50f5b1 100644
--- a/lib/chef/resource/openssl_rsa_private_key.rb
+++ b/lib/chef/resource/openssl_rsa_private_key.rb
@@ -43,7 +43,7 @@ class Chef
Generate new 1024bit key with the aes-128-cbc cipher
```ruby
- openssl_rsa_key '/etc/ssl_files/rsakey_aes128cbc.pem' do
+ openssl_rsa_private_key '/etc/ssl_files/rsakey_aes128cbc.pem' do
key_length 1024
key_cipher 'aes-128-cbc'
action :create
@@ -87,9 +87,7 @@ class Chef
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."
-
+ action :create, description: "Create the RSA private key file." do
return if new_resource.force || priv_key_file_valid?(new_resource.path, new_resource.key_pass)
converge_by("create #{new_resource.key_length} bit RSA key #{new_resource.path}") do
diff --git a/lib/chef/resource/openssl_rsa_public_key.rb b/lib/chef/resource/openssl_rsa_public_key.rb
index 8fd8ab558e..54362b45a4 100644
--- a/lib/chef/resource/openssl_rsa_public_key.rb
+++ b/lib/chef/resource/openssl_rsa_public_key.rb
@@ -75,9 +75,7 @@ class Chef
description: "The permission mode applied to all files created by the resource.",
default: "0640"
- action :create do
- description "Create the RSA public key."
-
+ action :create, description: "Create the RSA public key file." do
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)
diff --git a/lib/chef/resource/openssl_x509_certificate.rb b/lib/chef/resource/openssl_x509_certificate.rb
index c723f47d61..a6e0eb97f2 100644
--- a/lib/chef/resource/openssl_x509_certificate.rb
+++ b/lib/chef/resource/openssl_x509_certificate.rb
@@ -108,11 +108,11 @@ class Chef
property :extensions, Hash,
description: "Hash of X509 Extensions entries, in format `{ 'keyUsage' => { 'values' => %w( keyEncipherment digitalSignature), 'critical' => true } }`.",
- default: lazy { {} }
+ default: {}
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 { [] }
+ default: []
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."
@@ -151,15 +151,12 @@ class Chef
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"
-
+ action :create, description: "Generate a certificate file." do
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
diff --git a/lib/chef/resource/openssl_x509_crl.rb b/lib/chef/resource/openssl_x509_crl.rb
index 6e7f905084..ecf9778f25 100644
--- a/lib/chef/resource/openssl_x509_crl.rb
+++ b/lib/chef/resource/openssl_x509_crl.rb
@@ -90,9 +90,7 @@ class Chef
property :mode, [Integer, String],
description: "The permission mode of the CRL file."
- action :create do
- description "Create the CRL file."
-
+ action :create, description: "Create the certificate revocation list (CRL) file." do
file new_resource.path do
owner new_resource.owner unless new_resource.owner.nil?
group new_resource.group unless new_resource.group.nil?
diff --git a/lib/chef/resource/openssl_x509_request.rb b/lib/chef/resource/openssl_x509_request.rb
index 0e68337b05..378c484028 100644
--- a/lib/chef/resource/openssl_x509_request.rb
+++ b/lib/chef/resource/openssl_x509_request.rb
@@ -119,9 +119,7 @@ class Chef
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."
-
+ action :create, description: "Generate a certificate request file." do
unless ::File.exist? new_resource.path
converge_by("Create CSR #{@new_resource}") do
file new_resource.path do
diff --git a/lib/chef/resource/osx_profile.rb b/lib/chef/resource/osx_profile.rb
index 6c0028301d..ebeb8e34ca 100644
--- a/lib/chef/resource/osx_profile.rb
+++ b/lib/chef/resource/osx_profile.rb
@@ -51,7 +51,7 @@ class Chef
'PayloadOrganization' => 'Chef',
'PayloadVersion' => 1,
'PayloadDisplayName' => 'Screensaver Settings',
- 'PayloadContent'=> [
+ 'PayloadContent' => [
{
'PayloadType' => 'com.apple.ManagedClient.preferences',
'PayloadVersion' => 1,
@@ -65,13 +65,13 @@ class Chef
{
'mcx_preference_settings' => {
'idleTime' => 0,
- }
- }
- ]
- }
- }
- }
- ]
+ },
+ },
+ ],
+ },
+ },
+ },
+ ],
}
osx_profile 'Install screensaver profile' do
@@ -172,7 +172,7 @@ class Chef
end
end
- action :install do
+ action :install, description: "Install the specified configuration profile." do
unless profile_installed?
converge_by("install profile #{new_profile_identifier}") do
profile_path = write_profile_to_disk
@@ -182,7 +182,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove the specified configuration profile." do
# Clean up profile after removing it
if profile_installed?
converge_by("remove profile #{new_profile_identifier}") do
diff --git a/lib/chef/resource/plist.rb b/lib/chef/resource/plist.rb
index a7cb88ef57..4a43605607 100644
--- a/lib/chef/resource/plist.rb
+++ b/lib/chef/resource/plist.rb
@@ -64,24 +64,24 @@ class Chef
"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
+ load_current_value do |new_resource|
+ current_value_does_not_exist! unless ::File.exist? new_resource.path
+ entry new_resource.entry if entry_in_plist? new_resource.entry, new_resource.path
- setting = setting_from_plist desired.entry, desired.path
+ setting = setting_from_plist new_resource.entry, new_resource.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
+ file_type_cmd = shell_out "/usr/bin/file", "--brief", "--mime-encoding", "--preserve-date", new_resource.path
encoding file_type_cmd.stdout.chomp
- file_owner_cmd = shell_out("/usr/bin/stat", "-f", "%Su", desired.path)
+ file_owner_cmd = shell_out("/usr/bin/stat", "-f", "%Su", new_resource.path)
owner file_owner_cmd.stdout.chomp
- file_group_cmd = shell_out("/usr/bin/stat", "-f", "%Sg", desired.path)
+ file_group_cmd = shell_out("/usr/bin/stat", "-f", "%Sg", new_resource.path)
group file_group_cmd.stdout.chomp
end
- action :set do
+ action :set, description: "Set a value in a plist file." do
converge_if_changed :path do
converge_by "create new plist: '#{new_resource.path}'" do
file new_resource.path do
diff --git a/lib/chef/resource/powershell_package_source.rb b/lib/chef/resource/powershell_package_source.rb
index 066efc6a72..c4c4d2c841 100644
--- a/lib/chef/resource/powershell_package_source.rb
+++ b/lib/chef/resource/powershell_package_source.rb
@@ -70,8 +70,7 @@ class Chef
script_publish_location status["script_publish_location"]
end
- action :register do
- description "Registers and updates the powershell package source."
+ action :register, description: "Registers and updates the PowerShell package source." do
# TODO: Ensure package provider is installed?
if psrepository_cmdlet_appropriate?
if package_source_exists?
@@ -104,8 +103,7 @@ class Chef
end
end
- action :unregister do
- description "Unregisters the powershell package source."
+ action :unregister, description: "Unregisters the PowerShell package source." do
if package_source_exists?
unregister_cmd = "Get-PackageSource -Name '#{new_resource.source_name}' | Unregister-PackageSource"
converge_by("unregister source: #{new_resource.source_name}") do
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index eb72518009..b0be81feaa 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -27,7 +27,7 @@ class Chef
provides :powershell_script, os: "windows"
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.
+ 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 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
diff --git a/lib/chef/resource/reboot.rb b/lib/chef/resource/reboot.rb
index 6ac19e299b..718fb8dd51 100644
--- a/lib/chef/resource/reboot.rb
+++ b/lib/chef/resource/reboot.rb
@@ -33,6 +33,41 @@ class Chef
" immediate notifications. Delayed notifications produce unintuitive and"\
" probably undesired results."
introduced "12.0"
+ examples <<~DOC
+ **Reboot a node immediately**
+
+ ```ruby
+ reboot 'now' do
+ action :nothing
+ reason 'Cannot continue Chef run without a reboot.'
+ delay_mins 2
+ end
+
+ execute 'foo' do
+ command '...'
+ notifies :reboot_now, 'reboot[now]', :immediately
+ end
+ ```
+
+ **Reboot a node at the end of a Chef Infra Client run**
+
+ ```ruby
+ reboot 'app_requires_reboot' do
+ action :request_reboot
+ reason 'Need to reboot when the run completes successfully.'
+ delay_mins 5
+ end
+ ```
+
+ **Cancel a reboot**
+
+ ```ruby
+ reboot 'cancel_reboot_request' do
+ action :cancel
+ reason 'Cancel a previous end-of-run reboot request.'
+ end
+ ```
+ DOC
property :reason, String,
description: "A string that describes the reboot action.",
@@ -42,18 +77,14 @@ class Chef
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."
-
+ action :request_reboot, description: "Reboot a node at the end of a #{ChefUtils::Dist::Infra::PRODUCT} run." do
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."
-
+ action :reboot_now, description: "Reboot a node so that the #{ChefUtils::Dist::Infra::PRODUCT} may continue the installation process." do
converge_by("rebooting the system immediately") do
logger.warn "Rebooting system immediately, requested by '#{new_resource.name}'"
request_reboot
@@ -61,9 +92,7 @@ class Chef
end
end
- action :cancel do
- description "Cancel a pending reboot request."
-
+ action :cancel, description: "Cancel a pending reboot request." do
converge_by("cancel any existing end-of-run reboot request") do
logger.warn "Reboot canceled: '#{new_resource.name}'"
node.run_context.cancel_reboot
diff --git a/lib/chef/resource/remote_directory.rb b/lib/chef/resource/remote_directory.rb
index b6dc0b7a98..e7640e686e 100644
--- a/lib/chef/resource/remote_directory.rb
+++ b/lib/chef/resource/remote_directory.rb
@@ -29,7 +29,7 @@ class Chef
provides :remote_directory
- description "Use the **remote_directory** resource to incrementally transfer a directory from a cookbook to a node. The director that is copied from the cookbook should be located under COOKBOOK_NAME/files/default/REMOTE_DIRECTORY. The remote_directory resource will obey file specificity."
+ 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
@@ -71,7 +71,7 @@ class Chef
desired_state: false
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).",
+ 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]
property :files_mode, [String, Integer, nil],
@@ -80,7 +80,7 @@ class Chef
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).",
+ 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
diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb
index ac0b2fe6a7..8eca1c3337 100644
--- a/lib/chef/resource/remote_file.rb
+++ b/lib/chef/resource/remote_file.rb
@@ -95,7 +95,7 @@ class Chef
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 { {} },
+ property :headers, Hash, default: {},
description: "A Hash of custom HTTP headers."
property :show_progress, [ TrueClass, FalseClass ], default: false
@@ -142,13 +142,13 @@ class Chef
end
# if domain is provided in both username and domain
- if specified_user && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_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('\\')
+ domain_and_user = user.split("\\")
if domain_and_user.length == 2
domain = domain_and_user[0]
diff --git a/lib/chef/resource/rhsm_errata.rb b/lib/chef/resource/rhsm_errata.rb
index 7a1e3df325..d3e4a8b8b2 100644
--- a/lib/chef/resource/rhsm_errata.rb
+++ b/lib/chef/resource/rhsm_errata.rb
@@ -25,14 +25,27 @@ class Chef
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"
+ examples <<~DOC
+ **Install a package from an Errata ID**
+
+ ```ruby
+ rhsm_errata 'RHSA:2018-1234'
+ ```
+
+ **Specify an Errata ID that differs from the resource name**
+
+ ```ruby
+ rhsm_errata 'errata-install'
+ errata_id 'RHSA:2018-1234'
+ end
+ ```
+ DOC
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."
-
+ action :install, description: "Install a package for a specific errata ID." do
execute "Install errata packages for #{new_resource.errata_id}" do
command "#{package_manager_command} update --advisory #{new_resource.errata_id} -y"
default_env true
diff --git a/lib/chef/resource/rhsm_errata_level.rb b/lib/chef/resource/rhsm_errata_level.rb
index 9ac3944153..52d3458d91 100644
--- a/lib/chef/resource/rhsm_errata_level.rb
+++ b/lib/chef/resource/rhsm_errata_level.rb
@@ -25,6 +25,15 @@ class Chef
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"
+ examples <<~DOC
+ **Specify an errata level that differs from the resource name**
+
+ ```ruby
+ rhsm_errata_level 'example_install_moderate' do
+ errata_level 'moderate'
+ end
+ ```
+ DOC
property :errata_level, String,
coerce: proc { |x| x.downcase },
@@ -32,12 +41,8 @@ class Chef
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
+ action :install, description: "Install all packages of the specified errata level." do
+ yum_package "yum-plugin-security" if rhel6?
execute "Install any #{new_resource.errata_level} errata" do
command "#{package_manager_command} update --sec-severity=#{new_resource.errata_level.capitalize} -y"
diff --git a/lib/chef/resource/rhsm_register.rb b/lib/chef/resource/rhsm_register.rb
index 07c4dbc8d7..3e1d251e76 100644
--- a/lib/chef/resource/rhsm_register.rb
+++ b/lib/chef/resource/rhsm_register.rb
@@ -27,6 +27,16 @@ class Chef
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"
+ examples <<~DOC
+ **Register a node with RHSM*
+
+ ```ruby
+ rhsm_register 'my-host' do
+ activation_key 'ABCD1234'
+ organization 'my_org'
+ end
+ ```
+ DOC
property :activation_key, [String, Array],
coerce: proc { |x| Array(x) },
@@ -69,9 +79,7 @@ class Chef
default: false, desired_state: false,
introduced: "15.9"
- action :register do
- description "Register the node with RHSM."
-
+ action :register, description: "Register the node with RHSM." do
package "subscription-manager"
unless new_resource.satellite_host.nil? || registered_with_rhsm?
@@ -106,9 +114,7 @@ class Chef
end
end
- action :unregister do
- description "Unregister the node from RHSM."
-
+ action :unregister, description: "Unregister the node from RHSM." do
execute "Unregister from RHSM" do
command "subscription-manager unregister"
default_env true
diff --git a/lib/chef/resource/rhsm_repo.rb b/lib/chef/resource/rhsm_repo.rb
index d8959695cf..03567e004b 100644
--- a/lib/chef/resource/rhsm_repo.rb
+++ b/lib/chef/resource/rhsm_repo.rb
@@ -26,14 +26,27 @@ class Chef
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"
+ examples <<~DOC
+ **Enable an RHSM repository**
+
+ ```ruby
+ rhsm_repo 'rhel-7-server-extras-rpms'
+ ```
+
+ **Disable an RHSM repository**
+
+ ```ruby
+ rhsm_repo 'rhel-7-server-extras-rpms' do
+ action :disable
+ end
+ ```
+ DOC
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."
-
+ action :enable, description: "Enable a RHSM repository." do
execute "Enable repository #{new_resource.repo_name}" do
command "subscription-manager repos --enable=#{new_resource.repo_name}"
default_env true
@@ -42,9 +55,7 @@ class Chef
end
end
- action :disable do
- description "Disable a RHSM repository."
-
+ action :disable, description: "Disable a RHSM repository." do
execute "Enable repository #{new_resource.repo_name}" do
command "subscription-manager repos --disable=#{new_resource.repo_name}"
default_env true
diff --git a/lib/chef/resource/rhsm_subscription.rb b/lib/chef/resource/rhsm_subscription.rb
index 15a4822ecd..5ae04bbfcd 100644
--- a/lib/chef/resource/rhsm_subscription.rb
+++ b/lib/chef/resource/rhsm_subscription.rb
@@ -31,9 +31,7 @@ class Chef
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."
-
+ action :attach, description: "Attach the node to a subscription pool." do
execute "Attach subscription pool #{new_resource.pool_id}" do
command "subscription-manager attach --pool=#{new_resource.pool_id}"
default_env true
@@ -42,9 +40,7 @@ class Chef
end
end
- action :remove do
- description "Remove the node from a subscription pool."
-
+ action :remove, description: "Remove the node from a subscription pool." do
execute "Remove subscription pool #{new_resource.pool_id}" do
command "subscription-manager remove --serial=#{pool_serial(new_resource.pool_id)}"
default_env true
diff --git a/lib/chef/resource/ruby.rb b/lib/chef/resource/ruby.rb
index a9f3ae24fd..2c0e65e9da 100644
--- a/lib/chef/resource/ruby.rb
+++ b/lib/chef/resource/ruby.rb
@@ -25,11 +25,7 @@ class Chef
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."
+ 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
diff --git a/lib/chef/resource/ruby_block.rb b/lib/chef/resource/ruby_block.rb
index 427c3e25da..2d7d2fe8b6 100644
--- a/lib/chef/resource/ruby_block.rb
+++ b/lib/chef/resource/ruby_block.rb
@@ -28,7 +28,7 @@ class Chef
provides :ruby_block, target_mode: true
- 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."
+ 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
diff --git a/lib/chef/resource/scm/git.rb b/lib/chef/resource/scm/git.rb
index 8293d1ed4a..af49704ee6 100644
--- a/lib/chef/resource/scm/git.rb
+++ b/lib/chef/resource/scm/git.rb
@@ -112,7 +112,7 @@ class Chef
property :additional_remotes, Hash,
description: "A Hash of additional remotes that are added to the git repository configuration.",
- default: lazy { {} }
+ default: {}
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."
diff --git a/lib/chef/resource/ssh_known_hosts_entry.rb b/lib/chef/resource/ssh_known_hosts_entry.rb
index 1db811978c..a8d45f4c12 100644
--- a/lib/chef/resource/ssh_known_hosts_entry.rb
+++ b/lib/chef/resource/ssh_known_hosts_entry.rb
@@ -75,7 +75,8 @@ class Chef
property :group, [String, Integer],
description: "The file group for the ssh_known_hosts file.",
- default: lazy { node["root_group"] }
+ default: lazy { node["root_group"] },
+ default_description: "The root user's group depending on platform."
property :hash_entries, [TrueClass, FalseClass],
description: "Hash the hostname and addresses in the ssh_known_hosts file for privacy.",
@@ -85,9 +86,7 @@ class Chef
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."
-
+ action :create, description: "Create an entry in the ssh_known_hosts file." do
key =
if new_resource.key
hoststr = (new_resource.port != 22) ? "[#{new_resource.host}]:#{new_resource.port}" : new_resource.host
@@ -129,9 +128,7 @@ class Chef
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."
-
+ action :flush, 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." do
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)
diff --git a/lib/chef/resource/sudo.rb b/lib/chef/resource/sudo.rb
index d6587bd441..d028af80bf 100644
--- a/lib/chef/resource/sudo.rb
+++ b/lib/chef/resource/sudo.rb
@@ -71,12 +71,12 @@ class Chef
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 { [] },
+ default: [],
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 { [] },
+ default: [],
coerce: proc { |x| coerce_groups(x) }
property :commands, Array,
@@ -108,11 +108,11 @@ class Chef
property :defaults, Array,
description: "An array of defaults for the user/group.",
- default: lazy { [] }
+ default: []
property :command_aliases, Array,
description: "Command aliases that can be used as allowed commands later in the configuration.",
- default: lazy { [] }
+ default: []
property :setenv, [TrueClass, FalseClass],
description: "Determines whether or not to permit preservation of the environment with `sudo -E`.",
@@ -120,11 +120,11 @@ class Chef
property :env_keep_add, Array,
description: "An array of strings to add to `env_keep`.",
- default: lazy { [] }
+ default: []
property :env_keep_subtract, Array,
description: "An array of strings to remove from `env_keep`.",
- default: lazy { [] }
+ default: []
property :visudo_path, String,
deprecated: true
@@ -170,9 +170,7 @@ class Chef
end
end
- action :create do
- description "Create a single sudoers config in the sudoers.d directory"
-
+ action :create, description: "Create a single sudoers configuration file in the `sudoers.d` directory." do
validate_properties
if docker? # don't even put this into resource collection unless we're in docker
@@ -230,9 +228,7 @@ class Chef
end
# Removes a user from the sudoers group
- action :delete do
- description "Remove a sudoers config from the sudoers.d directory"
-
+ action :delete, description: "Remove a sudoers configuration file from the `sudoers.d` directory." do
file "#{new_resource.config_prefix}/sudoers.d/#{new_resource.filename}" do
action :delete
end
diff --git a/lib/chef/resource/swap_file.rb b/lib/chef/resource/swap_file.rb
index 3d8f31de48..bd2c7bf632 100644
--- a/lib/chef/resource/swap_file.rb
+++ b/lib/chef/resource/swap_file.rb
@@ -63,9 +63,7 @@ class Chef
property :swappiness, Integer,
description: "The swappiness value to set on the system."
- action :create do
- description "Create a swapfile."
-
+ action :create, description: "Create a swapfile." do
if swap_enabled?
Chef::Log.debug("#{new_resource} already created - nothing to do")
else
@@ -85,9 +83,7 @@ class Chef
end
end
- action :remove do
- description "Remove a swapfile and disable swap."
-
+ action :remove, description: "Remove a swapfile and disable swap." do
swapoff if swap_enabled?
remove_swapfile if ::File.exist?(new_resource.path)
end
diff --git a/lib/chef/resource/sysctl.rb b/lib/chef/resource/sysctl.rb
index bf9424f35f..9c36206e14 100644
--- a/lib/chef/resource/sysctl.rb
+++ b/lib/chef/resource/sysctl.rb
@@ -131,9 +131,7 @@ class Chef
end
- action :apply do
- description "Apply a sysctl value."
-
+ action :apply, description: "Apply a sysctl value." do
converge_if_changed do
# set it temporarily
set_sysctl_param(new_resource.key, new_resource.value)
@@ -152,9 +150,7 @@ class Chef
end
end
- action :remove do
- description "Remove a sysctl value."
-
+ action :remove, description: "Remove a sysctl value." do
# 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
diff --git a/lib/chef/resource/systemd_unit.rb b/lib/chef/resource/systemd_unit.rb
index b028214441..f6384ac947 100644
--- a/lib/chef/resource/systemd_unit.rb
+++ b/lib/chef/resource/systemd_unit.rb
@@ -34,7 +34,7 @@ class Chef
```ruby
systemd_unit 'etcd.service' do
- content({Unit: {
+ content(Unit: {
Description: 'Etcd',
Documentation: ['https://coreos.com/etcd', 'man:etcd(1)'],
After: 'network.target',
@@ -46,7 +46,7 @@ class Chef
},
Install: {
WantedBy: 'multi-user.target',
- }})
+ })
action [:create, :enable]
end
```
diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb
index 88fde45a25..60b431ab8e 100644
--- a/lib/chef/resource/template.rb
+++ b/lib/chef/resource/template.rb
@@ -61,7 +61,7 @@ class Chef
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 { {} }
+ default: {}
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.",
@@ -169,8 +169,8 @@ class Chef
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/timezone.rb b/lib/chef/resource/timezone.rb
index 04e5884b88..489b94b0b3 100644
--- a/lib/chef/resource/timezone.rb
+++ b/lib/chef/resource/timezone.rb
@@ -119,9 +119,7 @@ class Chef
end
end
- action :set do
- description "Set the timezone."
-
+ action :set, description: "Set the system timezone." do
# we have to check windows first since the value isn't case sensitive here
if windows?
unless current_windows_tz.casecmp?(new_resource.timezone)
diff --git a/lib/chef/resource/user/dscl_user.rb b/lib/chef/resource/user/dscl_user.rb
deleted file mode 100644
index 91efd657de..0000000000
--- a/lib/chef/resource/user/dscl_user.rb
+++ /dev/null
@@ -1,35 +0,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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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
- class DsclUser < Chef::Resource::User
- unified_mode true
-
- provides :dscl_user
- 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
-end
diff --git a/lib/chef/resource/user/mac_user.rb b/lib/chef/resource/user/mac_user.rb
index 2331283bbd..93eef5e893 100644
--- a/lib/chef/resource/user/mac_user.rb
+++ b/lib/chef/resource/user/mac_user.rb
@@ -61,7 +61,7 @@ class Chef
unified_mode true
provides :mac_user
- provides :user, platform: "mac_os_x", platform_version: ">= 10.14"
+ provides :user, platform: "mac_os_x"
introduced "15.3"
diff --git a/lib/chef/resource/user/windows_user.rb b/lib/chef/resource/user/windows_user.rb
index d504158edf..d738ba1636 100644
--- a/lib/chef/resource/user/windows_user.rb
+++ b/lib/chef/resource/user/windows_user.rb
@@ -29,6 +29,11 @@ class Chef
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
index d138eeabf3..55331dfc1c 100644
--- a/lib/chef/resource/user_ulimit.rb
+++ b/lib/chef/resource/user_ulimit.rb
@@ -78,7 +78,7 @@ class Chef
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
+ action :create, description: "Create a ulimit configuration file." do
template "/etc/security/limits.d/#{new_resource.filename}" do
source ::File.expand_path("support/ulimit.erb", __dir__)
local true
@@ -106,7 +106,7 @@ class Chef
end
end
- action :delete do
+ action :delete, description: "Delete an existing ulimit configuration file." do
file "/etc/security/limits.d/#{new_resource.filename}" do
action :delete
end
diff --git a/lib/chef/resource/windows_ad_join.rb b/lib/chef/resource/windows_ad_join.rb
index 731ce9333e..e285e1cf1e 100644
--- a/lib/chef/resource/windows_ad_join.rb
+++ b/lib/chef/resource/windows_ad_join.rb
@@ -97,9 +97,7 @@ class Chef
property :sensitive, [TrueClass, FalseClass],
default: true, desired_state: false
- action :join do
- description "Join the Active Directory domain."
-
+ action :join, description: "Join the Active Directory domain." do
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);"
@@ -129,9 +127,7 @@ class Chef
end
end
- action :leave do
- description "Leave the Active Directory domain."
-
+ action :leave, description: "Leave an Active Directory domain and re-join a workgroup." do
if joined_to_domain?
cmd = ""
cmd << "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;"
diff --git a/lib/chef/resource/windows_audit_policy.rb b/lib/chef/resource/windows_audit_policy.rb
index 433e18e197..b1fcbe2e44 100644
--- a/lib/chef/resource/windows_audit_policy.rb
+++ b/lib/chef/resource/windows_audit_policy.rb
@@ -106,7 +106,7 @@ class Chef
```ruby
windows_audit_policy "Set Audit Policy for 'Credential Validation' actions to 'Success'" do
- subcategory 'Credential Validation'
+ subcategory 'Credential Validation'
success true
failure false
action :set
@@ -152,7 +152,7 @@ class Chef
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
+ action :set, description: "Configure an audit policy." do
unless new_resource.subcategory.nil?
new_resource.subcategory.each do |subcategory|
next if subcategory_configured?(subcategory, new_resource.success, new_resource.failure)
diff --git a/lib/chef/resource/windows_auto_run.rb b/lib/chef/resource/windows_auto_run.rb
index 4885a02676..9e46b58b53 100644
--- a/lib/chef/resource/windows_auto_run.rb
+++ b/lib/chef/resource/windows_auto_run.rb
@@ -57,8 +57,7 @@ class Chef
alias_method :program, :path
- action :create do
- description "Create an item to be run at login."
+ action :create, description: "Create an item to be run at login." do
data = "\"#{new_resource.path}\""
data << " #{new_resource.args}" if new_resource.args
@@ -73,9 +72,7 @@ class Chef
end
end
- action :remove do
- description "Remove an item that was previously setup to run at login"
-
+ action :remove, description: "Remove an item that was previously configured to run at login." do
registry_key registry_path do
values [{
name: new_resource.program_name,
diff --git a/lib/chef/resource/windows_certificate.rb b/lib/chef/resource/windows_certificate.rb
index 2c8c7c72ff..528b0c53f6 100644
--- a/lib/chef/resource/windows_certificate.rb
+++ b/lib/chef/resource/windows_certificate.rb
@@ -19,6 +19,7 @@
require_relative "../util/path_helper"
require_relative "../resource"
+require_relative "../exceptions"
module Win32
autoload :Certstore, "win32-certstore" if Chef::Platform.windows?
end
@@ -62,44 +63,49 @@ class Chef
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.",
+ description: "The source file (for `create` and `acl_add`), thumbprint (for `delete`, `export`, and `acl_add`), or subject (for `delete` or `export`) 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."
+ description: "The password to access the object with 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."
+ 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.",
+ description: "Use the `CurrentUser` store instead of the default `LocalMachine` store. Note: Prior to #{ChefUtils::Dist::Infra::CLIENT}. 16.10 this property was ignored.",
default: false
- property :cert_path, String,
- description: "The path to the certificate."
+ deprecated_property_alias :cert_path, :output_path, "The cert_path property was renamed output_path in the 17.0 release of #{ChefUtils::Dist::Infra::CLIENT}. Please update your cookbooks to use the new property name."
# 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
- action :create do
- description "Creates or updates a certificate."
+ 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"
- # Extension of the certificate
- ext = ::File.extname(new_resource.source)
+ property :output_path, String,
+ description: "A path on the node where a certificate object (PFX, PEM, CER, KEY, etc) can be exported to.",
+ introduced: "17.0"
+
+ action :create, description: "Creates or updates a certificate." do
+ ext = get_file_extension(new_resource.source)
# PFX certificates contains private keys and we import them with some other approach
- import_certificates(fetch_cert_object(ext), (ext == ".pfx"))
+ # import_certificates(fetch_cert_object(ext), (ext == ".pfx"))
+ import_certificates(fetch_cert_object_from_file(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."
+ action :acl_add, description: "Adds read-only entries to a certificate's private key ACL." do
if ::File.exist?(new_resource.source)
hash = "$cert.GetCertHashString()"
@@ -114,7 +120,7 @@ class Chef
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
+ powershell_script "setting the acls on #{new_resource.source} in #{ps_cert_location}\\#{new_resource.store_name}" do
convert_boolean_return true
code code_script
only_if guard_script
@@ -122,9 +128,9 @@ class Chef
end
end
- action :delete do
- description "Deletes a certificate."
+ action :delete, description: "Deletes a certificate." do
cert_obj = fetch_cert
+
if cert_obj
converge_by("Deleting certificate #{new_resource.source} from Store #{new_resource.store_name}") do
delete_cert
@@ -134,20 +140,27 @@ class Chef
end
end
- action :fetch do
- description "Fetches a certificate."
+ action :fetch, description: "Fetches a certificate." do
+ unless new_resource.output_path
+ raise Chef::Exceptions::ResourceNotFound, "You must include an output_path parameter when calling the fetch action"
+ end
+
+ if ::File.extname(new_resource.output_path) == ".pfx"
+ powershell_exec!(pfx_ps_cmd(resolve_thumbprint(new_resource.source), store_location: ps_cert_location, store_name: new_resource.store_name, output_path: new_resource.output_path, password: new_resource.pfx_password ))
+ else
+ cert_obj = fetch_cert
+ end
- cert_obj = fetch_cert
if cert_obj
- show_or_store_cert(cert_obj)
+ converge_by("Fetching certificate #{new_resource.source} from Store \\#{ps_cert_location}\\#{new_resource.store_name}") do
+ export_cert(cert_obj, output_path: new_resource.output_path, store_name: new_resource.store_name , store_location: ps_cert_location, pfx_password: new_resource.pfx_password)
+ end
else
Chef::Log.debug("Certificate not found")
end
end
- action :verify do
- description ""
-
+ action :verify, description: "Verifies a certificate and logs the result." do
out = verify_cert
if !!out == out
out = out ? "Certificate is valid" : "Certificate not valid"
@@ -156,24 +169,102 @@ class Chef
end
action_class do
+ @local_pfx_path = ""
+
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
+
def add_cert(cert_obj)
- store = ::Win32::Certstore.open(new_resource.store_name)
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
store.add(cert_obj)
end
- def add_pfx_cert
- store = ::Win32::Certstore.open(new_resource.store_name)
- store.add_pfx(new_resource.source, new_resource.pfx_password)
+ def add_pfx_cert(path)
+ exportable = new_resource.exportable ? 1 : 0
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
+ store.add_pfx(path, new_resource.pfx_password, exportable)
end
def delete_cert
- store = ::Win32::Certstore.open(new_resource.store_name)
- store.delete(new_resource.source)
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
+ store.delete(resolve_thumbprint(new_resource.source))
end
def fetch_cert
- store = ::Win32::Certstore.open(new_resource.store_name)
- store.get(new_resource.source)
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
+ if new_resource.output_path && ::File.extname(new_resource.output_path) == ".key"
+ fetch_key
+
+ else
+ store.get(resolve_thumbprint(new_resource.source), store_name: new_resource.store_name, store_location: native_cert_location)
+ end
+ end
+
+ def fetch_key
+ require "openssl" unless defined?(OpenSSL)
+ file_name = ::File.basename(new_resource.output_path, ::File.extname(new_resource.output_path))
+ directory = ::File.dirname(new_resource.output_path)
+ pfx_file = file_name + ".pfx"
+ new_pfx_output_path = ::File.join(Chef::FileCache.create_cache_path("pfx_files"), pfx_file)
+ powershell_exec(pfx_ps_cmd(resolve_thumbprint(new_resource.source), store_location: ps_cert_location, store_name: new_resource.store_name, output_path: new_pfx_output_path, password: new_resource.pfx_password ))
+ pkcs12 = OpenSSL::PKCS12.new(::File.binread(new_pfx_output_path), new_resource.pfx_password)
+ f = ::File.open(new_resource.output_path, "w")
+ f.write(pkcs12.key.to_s)
+ f.flush
+ f.close
+ end
+
+ def get_file_extension(file_name)
+ if is_file?(file_name)
+ ::File.extname(file_name)
+ elsif is_url?(file_name)
+ require "open-uri" unless defined?(OpenURI)
+ uri = URI.parse(file_name)
+ output_file = ::File.basename(uri.path)
+ ::File.extname(output_file)
+ end
+ end
+
+ def get_file_name(path_name)
+ if is_file?(path_name)
+ ::File.extname(path_name)
+ elsif is_url?(path_name)
+ require "open-uri" unless defined?(OpenURI)
+ uri = URI.parse(path_name)
+ ::File.basename(uri.path)
+ end
+ end
+
+ def is_url?(source)
+ require "uri" unless defined?(URI)
+ uri = URI.parse(source)
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
+ end
+
+ def is_file?(source)
+ ::File.file?(source)
+ end
+
+ def is_file?(source)
+ ::File.file?(source)
+ end
+
+ # Thumbprints should be exactly 40 Hex characters
+ def valid_thumbprint?(string)
+ string.match?(/[0-9A-Fa-f]/) && string.length == 40
+ end
+
+ def get_thumbprint(store_name, location, source)
+ <<-GETTHUMBPRINTCODE
+ $content = Get-ChildItem -Path Cert:\\#{location}\\#{store_name} | Where-Object {$_.Subject -Match "#{source}"} | Select-Object Thumbprint
+ $content.thumbprint
+ GETTHUMBPRINTCODE
+ end
+
+ def resolve_thumbprint(thumbprint)
+ return thumbprint if valid_thumbprint?(thumbprint)
+
+ powershell_exec!(get_thumbprint(new_resource.store_name, ps_cert_location, new_resource.source)).result
end
# Checks whether a certificate with the given thumbprint
@@ -181,56 +272,38 @@ class Chef
# 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
+ # updated this method to accept either a subject name or a thumbprint - 1/29/2021
- 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
+ def verify_cert(thumbprint = new_resource.source)
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
+ if new_resource.pfx_password.nil?
+ store.valid?(resolve_thumbprint(thumbprint), store_location: native_cert_location, store_name: new_resource.store_name )
else
- Chef::Log.info(cert_obj.display)
+ store.valid?(resolve_thumbprint(thumbprint), store_location: native_cert_location, store_name: new_resource.store_name)
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
+ # this array structure is solving 2 problems. The first is that we need to have support for both the CurrentUser AND LocalMachine stores
+ # Secondly, we need to pass the proper constant name for each store to win32-certstore but also pass the short name to powershell scripts used here
+ def ps_cert_location
+ new_resource.user_store ? "CurrentUser" : "LocalMachine"
+ end
+
+ def pfx_ps_cmd(thumbprint, store_location: "LocalMachine", store_name: "My", output_path:, password: )
+ <<-CMD
+ $my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText
+ $cert = Get-ChildItem -path cert:\\#{store_location}\\#{store_name} -Recurse | Where { $_.Thumbprint -eq "#{thumbprint.upcase}" }
+ Export-PfxCertificate -Cert $cert -FilePath "#{output_path}" -Password $my_pwd
+ CMD
end
- def cert_location
- @location ||= new_resource.user_store ? "CurrentUser" : "LocalMachine"
+ def native_cert_location
+ new_resource.user_store ? CERT_SYSTEM_STORE_CURRENT_USER : CERT_SYSTEM_STORE_LOCAL_MACHINE
end
def cert_script(persist)
cert_script = "$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2"
- file = Chef::Util::PathHelper.cleanpath(new_resource.source)
+ file = Chef::Util::PathHelper.cleanpath(new_resource.source, ps_cert_location)
cert_script << " \"#{file}\""
if ::File.extname(file.downcase) == ".pfx"
cert_script << ", \"#{new_resource.pfx_password}\""
@@ -246,14 +319,14 @@ class Chef
def cert_exists_script(hash)
<<-EOH
$hash = #{hash}
- Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
+ Test-Path "Cert:\\#{ps_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 = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{ps_cert_location})
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
#{inner_script}
$store.Close()
@@ -267,7 +340,7 @@ class Chef
# 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"
+ $storeCert = Get-ChildItem "cert:\\#{ps_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.' }
@@ -302,12 +375,50 @@ class Chef
#
# @raise [OpenSSL::PKCS12::PKCS12Error] When incorrect password is provided for PFX certificate
#
- def fetch_cert_object(ext)
- contents = if binary_cert?
- ::File.binread(new_resource.source)
- else
- ::File.read(new_resource.source)
- end
+
+ def fetch_cert_object_from_file(ext)
+ if is_file?(new_resource.source)
+ begin
+ ::File.exist?(new_resource.source)
+ contents = ::File.binread(new_resource.source)
+ rescue => exception
+ message = "Unable to load the certificate object from the specified local path : #{new_resource.source}\n"
+ message << exception.message
+ raise Chef::Exceptions::FileNotFound, message
+ end
+ elsif is_url?(new_resource.source)
+ require "uri" unless defined?(URI)
+ uri = URI(new_resource.source)
+ state = uri.is_a?(URI::HTTP) && !uri.host.nil? ? true : false
+ if state
+ begin
+ output_file_name = get_file_name(new_resource.source)
+ unless Dir.exist?(Chef::Config[:file_cache_path])
+ Dir.mkdir(Chef::Config[:file_cache_path])
+ end
+ local_path = ::File.join(Chef::Config[:file_cache_path], output_file_name)
+ @local_pfx_path = local_path
+ ::File.open(local_path, "wb") do |file|
+ file.write URI.open(new_resource.source).read
+ end
+ rescue => exception
+ message = "Not Able to Download Certificate Object at the URL specified : #{new_resource.source}\n"
+ message << exception.message
+ raise Chef::Exceptions::FileNotFound, message
+ end
+
+ contents = ::File.binread(local_path)
+
+ else
+ message = "Not Able to Download Certificate Object at the URL specified : #{new_resource.source}\n"
+ message << exception.message
+ raise Chef::Exceptions::InvalidRemoteFileURI, message
+ end
+ else
+ message = "You passed an invalid file or url to import. Please check the spelling and try again."
+ message << exception.message
+ raise Chef::Exceptions::ArgumentError, message
+ end
case ext
when ".pfx"
@@ -324,10 +435,42 @@ class Chef
end
end
- # @return [Boolean] Whether the certificate file is binary encoded or not
- #
- def binary_cert?
- shell_out!("file -b --mime-encoding #{new_resource.source}").stdout.strip == "binary"
+ def export_cert(cert_obj, output_path:, store_name:, store_location:, pfx_password:)
+ # Delete the cert if it exists. This is non-destructive in that it only removes the file and not the entire path.
+ # We want to ensure we're not randomly loading an old stinky cert.
+ if ::File.exists?(output_path)
+ ::File.delete(output_path)
+ end
+
+ unless ::File.directory?(::File.dirname(output_path))
+ FileUtils.mkdir_p(::File.dirname(output_path))
+ end
+
+ out_file = ::File.new(output_path, "w+")
+
+ case ::File.extname(output_path)
+ when ".pem"
+ out_file.puts(cert_obj)
+ 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} -outform CRT").stdout
+ out_file.puts(cert_out)
+ when ".pfx"
+ pfx_ps_cmd(resolve_thumbprint(new_resource.source), store_location: store_location, store_name: store_name, output_path: output_path, password: pfx_password )
+ when ".p7b"
+ cert_out = shell_out("openssl pkcs7 -export -nokeys -in #{cert_obj.to_pem} -outform P7B").stdout
+ out_file.puts(cert_out)
+ when ".key"
+ out_file.puts(cert_obj)
+ else
+ Chef::Log.info("Supported certificate format .pem, .der, .cer, .crt, and .p7b")
+ end
+
+ out_file.close
end
# Imports the certificate object into cert store
@@ -336,18 +479,35 @@ class Chef
#
# @param is_pfx [Boolean] true if we want to import a PFX certificate
#
- def import_certificates(cert_objs, is_pfx)
+ def import_certificates(cert_objs, is_pfx, store_name: new_resource.store_name, store_location: native_cert_location)
[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
+ # thumbprint = OpenSSL::Digest.new("SHA1", cert_obj.to_der).to_s
+ # pkcs = OpenSSL::PKCS12.new(cert_obj, new_resource.pfx_password)
+ # cert = OpenSSL::X509::Certificate.new(pkcs.certificate.to_pem)
+ thumbprint = OpenSSL::Digest.new("SHA1", cert_obj.to_der).to_s
+ if is_pfx
+ if verify_cert(thumbprint) == true
+ Chef::Log.debug("Certificate is already present")
+ else
+ if is_file?(new_resource.source)
+ converge_by("Creating a PFX #{new_resource.source} for Store #{new_resource.store_name}") do
+ add_pfx_cert(new_resource.source)
+ end
+ elsif is_url?(new_resource.source)
+ converge_by("Creating a PFX #{@local_pfx_path} for Store #{new_resource.store_name}") do
+ add_pfx_cert(@local_pfx_path)
+ end
else
+ message = "You passed an invalid file or url to import. Please check the spelling and try again."
+ message << exception.message
+ raise Chef::Exceptions::ArgumentError, message
+ end
+ end
+ else
+ if verify_cert(thumbprint) == true
+ Chef::Log.debug("Certificate is already present")
+ else
+ converge_by("Creating a certificate #{new_resource.source} for Store #{new_resource.store_name}") do
add_cert(cert_obj)
end
end
diff --git a/lib/chef/resource/windows_defender.rb b/lib/chef/resource/windows_defender.rb
new file mode 100644
index 0000000000..23894ac4a5
--- /dev/null
+++ b/lib/chef/resource/windows_defender.rb
@@ -0,0 +1,163 @@
+#
+# 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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 WindowsDefender < Chef::Resource
+ unified_mode true
+ provides :windows_defender
+
+ description "Use the **windows_defender** resource to enable or disable the Microsoft Windows Defender service."
+ introduced "17.3"
+ examples <<~DOC
+ **Configure Windows Defender AV settings**:
+
+ ```ruby
+ windows_defender 'Configure Defender' do
+ realtime_protection true
+ intrusion_protection_system true
+ lock_ui true
+ scan_archives true
+ scan_scripts true
+ scan_email true
+ scan_removable_drives true
+ scan_network_files false
+ scan_mapped_drives false
+ action :enable
+ end
+ ```
+
+ **Disable Windows Defender AV**:
+
+ ```ruby
+ windows_defender 'Disable Defender' do
+ action :disable
+ end
+ ```
+ DOC
+
+ # DisableIOAVProtection
+ property :realtime_protection, [true, false],
+ default: true,
+ description: "Enable realtime scanning of downloaded files and attachments."
+
+ # DisableIntrusionPreventionSystem
+ property :intrusion_protection_system, [true, false],
+ default: true,
+ description: "Enable network protection against exploitation of known vulnerabilities."
+
+ # UILockdown
+ property :lock_ui, [true, false],
+ description: "Lock the UI to prevent users from changing Windows Defender settings.",
+ default: false
+
+ # DisableArchiveScanning
+ property :scan_archives, [true, false],
+ default: true,
+ description: "Scan file archives such as .zip or .gz archives."
+
+ # DisableScriptScanning
+ property :scan_scripts, [true, false],
+ default: false,
+ description: "Scan scripts in malware scans."
+
+ # DisableEmailScanning
+ property :scan_email, [true, false],
+ default: false,
+ description: "Scan e-mails for malware."
+
+ # DisableRemovableDriveScanning
+ property :scan_removable_drives, [true, false],
+ default: false,
+ description: "Scan content of removable drives."
+
+ # DisableScanningNetworkFiles
+ property :scan_network_files, [true, false],
+ default: false,
+ description: "Scan files on a network."
+
+ # DisableScanningMappedNetworkDrivesForFullScan
+ property :scan_mapped_drives, [true, false],
+ default: true,
+ description: "Scan files on mapped network drives."
+
+ load_current_value do
+ values = powershell_exec!("Get-MPpreference").result
+
+ lock_ui values["UILockdown"]
+ realtime_protection !values["DisableIOAVProtection"]
+ intrusion_protection_system !values["DisableIntrusionPreventionSystem"]
+ scan_archives !values["DisableArchiveScanning"]
+ scan_scripts !values["DisableScriptScanning"]
+ scan_email !values["DisableEmailScanning"]
+ scan_removable_drives !values["DisableRemovableDriveScanning"]
+ scan_network_files !values["DisableScanningNetworkFiles"]
+ scan_mapped_drives !values["DisableScanningMappedNetworkDrivesForFullScan"]
+ end
+
+ action :enable do
+ windows_service "Windows Defender" do
+ service_name "WinDefend"
+ action %i{start enable}
+ startup_type :automatic
+ end
+
+ converge_if_changed do
+ powershell_exec!(set_mppreference_cmd)
+ end
+ end
+
+ action :disable do
+ windows_service "Windows Defender" do
+ service_name "WinDefend"
+ action %i{disable stop}
+ end
+ end
+
+ action_class do
+ require "chef/mixin/powershell_type_coercions"
+ include Chef::Mixin::PowershellTypeCoercions
+
+ PROPERTY_TO_PS_MAP = {
+ realtime_protection: "DisableIOAVProtection",
+ intrusion_protection_system: "DisableIntrusionPreventionSystem",
+ scan_archives: "DisableArchiveScanning",
+ scan_scripts: "DisableScriptScanning",
+ scan_email: "DisableEmailScanning",
+ scan_removable_drives: "DisableRemovableDriveScanning",
+ scan_network_files: "DisableScanningNetworkFiles",
+ scan_mapped_drives: "DisableScanningMappedNetworkDrivesForFullScan",
+ }.freeze
+
+ def set_mppreference_cmd
+ cmd = "Set-MpPreference -Force"
+ cmd << " -UILockdown #{type_coercion(new_resource.lock_ui)}"
+
+ # the values are the opposite in Set-MpPreference and our properties so we have to iterate
+ # over the list and negate the provided values so it makes sense with the cmdlet flag's expected value
+ PROPERTY_TO_PS_MAP.each do |prop, flag|
+ next if new_resource.send(prop).nil? || current_resource.send(prop) == new_resource.send(prop)
+
+ cmd << " -#{flag} #{type_coercion(!new_resource.send(prop))}"
+ end
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_defender_exclusion.rb b/lib/chef/resource/windows_defender_exclusion.rb
new file mode 100644
index 0000000000..ad9afd77e2
--- /dev/null
+++ b/lib/chef/resource/windows_defender_exclusion.rb
@@ -0,0 +1,125 @@
+#
+# 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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 WindowsDefenderExclusion < Chef::Resource
+
+ provides :windows_defender_exclusion
+
+ description "Use the **windows_defender_exclusion** resource to exclude paths, processes, or file types from Windows Defender realtime protection scanning."
+ introduced "17.3"
+ examples <<~DOC
+ **Add excluded items to Windows Defender scans**:
+
+ ```ruby
+ windows_defender_exclusion 'Add to things to be excluded from scanning' do
+ paths 'c:\\foo\\bar, d:\\bar\\baz'
+ extensions 'png, foo, ppt, doc'
+ process_paths 'c:\\windows\\system32'
+ action :add
+ end
+ ```
+
+ **Remove excluded items from Windows Defender scans**:
+
+ ```ruby
+ windows_defender_exclusion 'Remove things from the list to be excluded' do
+ process_paths 'c:\\windows\\system32'
+ action :remove
+ end
+ ```
+ DOC
+ unified_mode true
+
+ property :paths, [String, Array], default: [],
+ coerce: proc { |x| to_consistent_path_array(x) },
+ description: "File or directory paths to exclude from scanning."
+
+ property :extensions, [String, Array], default: [],
+ coerce: proc { |x| to_consistent_path_array(x) },
+ description: "File extensions to exclude from scanning."
+
+ property :process_paths, [String, Array], default: [],
+ coerce: proc { |x| to_consistent_path_array(x) },
+ description: "Paths to executables to exclude from scanning."
+
+ def to_consistent_path_array(x)
+ fixed = x.dup || []
+ fixed = fixed.split(/\s*,\s*/) if fixed.is_a?(String)
+ fixed.map!(&:downcase)
+ fixed.map! { |v| v.gsub(%r{/}, "\\") }
+ fixed
+ end
+
+ load_current_value do |new_resource|
+ Chef::Log.debug("Running 'Get-MpPreference | Select-Object ExclusionExtension,ExclusionPath,ExclusionProcess' to get Windows Defender State")
+
+ values = powershell_exec!("Get-MPpreference | Select-Object ExclusionExtension,ExclusionPath,ExclusionProcess").result
+
+ values.transform_values! { |x| Array(x) }
+
+ paths new_resource.paths & values["ExclusionPath"]
+ extensions new_resource.extensions & values["ExclusionExtension"]
+ process_paths new_resource.process_paths & values["ExclusionProcess"]
+ end
+
+ action :add do
+ converge_if_changed do
+ powershell_exec!(add_cmd)
+ end
+ end
+
+ action :remove do
+ converge_if_changed do
+ powershell_exec!(remove_cmd)
+ end
+ end
+
+ action_class do
+ MAPPING = {
+ paths: "ExclusionPath",
+ extensions: "ExclusionExtension",
+ process_paths: "ExclusionProcess",
+ }.freeze
+
+ def add_cmd
+ cmd = "Add-MpPreference -Force"
+
+ MAPPING.each do |prop, flag|
+ to_add = new_resource.send(prop) - current_resource.send(prop)
+ cmd << " -#{flag} #{to_add.join(",")}" unless to_add.empty?
+ end
+
+ cmd
+ end
+
+ def remove_cmd
+ cmd = "Remove-MpPreference -Force"
+
+ MAPPING.each do |prop, flag|
+ to_add = new_resource.send(prop) & current_resource.send(prop)
+ cmd << " -#{flag} #{to_add.join(",")}" unless to_add.empty?
+ end
+
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dfs_folder.rb b/lib/chef/resource/windows_dfs_folder.rb
index 31f6814bcf..b9ef4525f9 100644
--- a/lib/chef/resource/windows_dfs_folder.rb
+++ b/lib/chef/resource/windows_dfs_folder.rb
@@ -42,9 +42,7 @@ class Chef
property :description, String,
description: "Description for the share."
- action :create do
- description "Creates the folder in dfs namespace."
-
+ action :create, description: "Creates the folder in dfs namespace." do
raise "target_path is required for install" unless property_is_set?(:target_path)
raise "description is required for install" unless property_is_set?(:description)
@@ -62,9 +60,7 @@ class Chef
end
end
- action :delete do
- description "Deletes the folder in the dfs namespace."
-
+ action :delete, description: "Deletes the folder in the dfs namespace." do
powershell_script "Delete DFS Namespace" do
code <<-EOH
Remove-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -Force
diff --git a/lib/chef/resource/windows_dfs_namespace.rb b/lib/chef/resource/windows_dfs_namespace.rb
index ddd8a0ee26..2fdfd8ebb6 100644
--- a/lib/chef/resource/windows_dfs_namespace.rb
+++ b/lib/chef/resource/windows_dfs_namespace.rb
@@ -52,9 +52,7 @@ class Chef
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."
-
+ action :create, description: "Creates the dfs namespace on the server." do
directory file_path do
action :create
recursive true
@@ -84,9 +82,7 @@ class Chef
end
end
- action :delete do
- description "Deletes a DFS Namespace including the directory on disk."
-
+ action :delete, description: "Deletes a DFS Namespace including the directory on disk." do
powershell_script "Delete DFS Namespace" do
code <<-EOH
Remove-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -Force
diff --git a/lib/chef/resource/windows_dfs_server.rb b/lib/chef/resource/windows_dfs_server.rb
index fc161f8189..e93185c245 100644
--- a/lib/chef/resource/windows_dfs_server.rb
+++ b/lib/chef/resource/windows_dfs_server.rb
@@ -65,9 +65,7 @@ class Chef
sync_interval_secs results["SyncIntervalSec"]
end
- action :configure do
- description "Configure DFS settings."
-
+ action :configure, description: "Configure DFS settings" do
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
diff --git a/lib/chef/resource/windows_dns_record.rb b/lib/chef/resource/windows_dns_record.rb
index 329e1a3857..8b908f09e6 100644
--- a/lib/chef/resource/windows_dns_record.rb
+++ b/lib/chef/resource/windows_dns_record.rb
@@ -49,9 +49,7 @@ class Chef
default: "localhost",
introduced: "16.3"
- action :create do
- description "Creates and updates the DNS entry."
-
+ action :create, description: "Creates and updates the DNS entry." do
windows_feature "RSAT-DNS-Server" do
not_if new_resource.dns_server.casecmp?("localhost")
end
@@ -61,9 +59,7 @@ class Chef
run_dsc_resource "Present"
end
- action :delete do
- description "Deletes a DNS entry."
-
+ action :delete, description: "Deletes a DNS entry." do
windows_feature "RSAT-DNS-Server" do
not_if new_resource.dns_server.casecmp?("localhost")
end
diff --git a/lib/chef/resource/windows_dns_zone.rb b/lib/chef/resource/windows_dns_zone.rb
index 09555c880c..eb7e42c3ce 100644
--- a/lib/chef/resource/windows_dns_zone.rb
+++ b/lib/chef/resource/windows_dns_zone.rb
@@ -40,17 +40,13 @@ class Chef
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."
-
+ action :create, description: "Creates and updates a DNS Zone." do
powershell_package "xDnsServer"
run_dsc_resource "Present"
end
- action :delete do
- description "Deletes a DNS Zone."
-
+ action :delete, description: "Deletes a DNS Zone." do
powershell_package "xDnsServer"
run_dsc_resource "Absent"
diff --git a/lib/chef/resource/windows_env.rb b/lib/chef/resource/windows_env.rb
index ab65465ed6..c8385ecc1b 100644
--- a/lib/chef/resource/windows_env.rb
+++ b/lib/chef/resource/windows_env.rb
@@ -19,6 +19,7 @@
require_relative "../resource"
require_relative "../mixin/windows_env_helper"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
@@ -28,7 +29,7 @@ class Chef
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."
+ 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.\n\nThis resource was previously called the **env** resource; its name was updated in #{ChefUtils::Dist::Infra::PRODUCT} 14.0 to reflect the fact that only Windows is supported. Existing cookbooks using `env` will continue to function, but should be updated to use the new name. Note: On UNIX-based systems, the best way to manipulate environment keys is with the `ENV` variable in Ruby; however, this approach does not have the same permanent effect as using the windows_env resource."
examples <<~DOC
**Set an environment variable**:
@@ -185,14 +186,14 @@ class Chef
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
+ return @env_obj if @env_obj.username.split("\\").last.casecmp(new_resource.user) == 0
end
end
@env_obj = nil
end
end
- action :create do
+ action :create, description: "Create an environment variable. If an environment variable already exists (but does not match), update that environment variable to match." do
if key_exists?
if requires_modify_or_create?
modify_env
@@ -206,7 +207,7 @@ class Chef
end
end
- action :delete do
+ action :delete, description: "Delete an environment variable." do
if ( ENV[new_resource.key_name] || key_exists? ) && !delete_element
delete_env
logger.info("#{new_resource} deleted")
@@ -214,7 +215,7 @@ class Chef
end
end
- action :modify do
+ action :modify, description: "Modify an existing environment variable. This prepends the new value to the existing value, using the delimiter specified by the `delim` property." do
if key_exists?
if requires_modify_or_create?
modify_env
diff --git a/lib/chef/resource/windows_feature.rb b/lib/chef/resource/windows_feature.rb
index 760a7fe3f1..ae71b299fe 100644
--- a/lib/chef/resource/windows_feature.rb
+++ b/lib/chef/resource/windows_feature.rb
@@ -108,21 +108,15 @@ class Chef
default: 600,
desired_state: false
- action :install do
- description "Install a Windows role/feature"
-
+ action :install, description: "Install a Windows role or feature." do
run_default_subresource :install
end
- action :remove do
- description "Remove a Windows role/feature"
-
+ action :remove, description: "Remove a Windows role or feature." do
run_default_subresource :remove
end
- action :delete do
- description "Remove a Windows role/feature from the image"
-
+ action :delete, description: "Remove a Windows role or feature from the image." do
run_default_subresource :delete
end
diff --git a/lib/chef/resource/windows_feature_dism.rb b/lib/chef/resource/windows_feature_dism.rb
index c9e2f355dc..a2d91eaede 100644
--- a/lib/chef/resource/windows_feature_dism.rb
+++ b/lib/chef/resource/windows_feature_dism.rb
@@ -65,9 +65,7 @@ class Chef
x.map(&:downcase)
end
- action :install do
- description "Install a Windows role/feature using DISM"
-
+ action :install, description: "Install a Windows role/feature using DISM." do
reload_cached_dism_data unless node["dism_features_cache"]
fail_if_unavailable # fail if the features don't exist
@@ -91,9 +89,7 @@ class Chef
end
end
- action :remove do
- description "Remove a Windows role/feature using DISM"
-
+ action :remove, description: "Remove a Windows role or feature using DISM." do
reload_cached_dism_data unless node["dism_features_cache"]
logger.trace("Windows features needing removal: #{features_to_remove.empty? ? "none" : features_to_remove.join(",")}")
@@ -108,9 +104,7 @@ class Chef
end
end
- action :delete do
- description "Remove a Windows role/feature from the image using DISM"
-
+ action :delete, description: "Remove a Windows role or feature from the image using DISM." do
reload_cached_dism_data unless node["dism_features_cache"]
fail_if_unavailable # fail if the features don't exist
diff --git a/lib/chef/resource/windows_feature_powershell.rb b/lib/chef/resource/windows_feature_powershell.rb
index 735ed080ff..e9889eb954 100644
--- a/lib/chef/resource/windows_feature_powershell.rb
+++ b/lib/chef/resource/windows_feature_powershell.rb
@@ -87,7 +87,7 @@ class Chef
x.map(&:downcase)
end
- action :install do
+ action :install, description: "Install a Windows role or feature using PowerShell." 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
@@ -108,7 +108,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a Windows role or feature using PowerShell." 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(",")}")
@@ -123,7 +123,7 @@ class Chef
end
end
- action :delete do
+ action :delete, description: "Delete a Windows role or feature from the image using PowerShell." do
reload_cached_powershell_data unless node["powershell_features_cache"]
fail_if_unavailable # fail if the features don't exist
diff --git a/lib/chef/resource/windows_firewall_profile.rb b/lib/chef/resource/windows_firewall_profile.rb
index ada9729699..573f4b2cfa 100644
--- a/lib/chef/resource/windows_firewall_profile.rb
+++ b/lib/chef/resource/windows_firewall_profile.rb
@@ -81,8 +81,8 @@ class Chef
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)
+ load_current_value do |new_resource|
+ ps_get_net_fw_profile = load_firewall_state(new_resource.profile)
output = powershell_exec(ps_get_net_fw_profile)
if output.result.empty?
current_value_does_not_exist!
@@ -121,7 +121,7 @@ class Chef
end
end
- action :enable do
+ action :enable, description: "Enable and optionally configure a Windows Firewall profile." 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)
@@ -135,7 +135,7 @@ class Chef
end
end
- action :disable do
+ action :disable, description: "Disable a Windows Firewall profile." 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\""
diff --git a/lib/chef/resource/windows_firewall_rule.rb b/lib/chef/resource/windows_firewall_rule.rb
index a6f0614362..e397a94670 100644
--- a/lib/chef/resource/windows_firewall_rule.rb
+++ b/lib/chef/resource/windows_firewall_rule.rb
@@ -39,6 +39,19 @@ class Chef
end
```
+ **Configuring multiple remote-address ports on a rule**:
+
+ ```ruby
+ windows_firewall_rule 'MyRule' do
+ description 'Testing out remote address arrays'
+ enabled false
+ local_port 1434
+ remote_address %w(10.17.3.101 172.7.7.53)
+ protocol 'TCP'
+ action :create
+ end
+ ```
+
**Allow protocol ICMPv6 with ICMP Type**:
```ruby
@@ -97,8 +110,9 @@ class Chef
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_address, [String, Array],
+ coerce: proc { |d| d.is_a?(String) ? d.split(/\s*,\s*/).sort : Array(d).sort.map(&:to_s) },
+ description: "The remote address(es) 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
@@ -172,7 +186,7 @@ class Chef
group state["group"]
local_address state["local_address"]
local_port Array(state["local_port"]).sort
- remote_address state["remote_address"]
+ remote_address Array(state["remote_address"]).sort
remote_port Array(state["remote_port"]).sort
direction state["direction"]
protocol state["protocol"]
@@ -185,8 +199,7 @@ class Chef
enabled state["enabled"]
end
- action :create do
- description "Create a Windows firewall entry."
+ action :create, description: "Create a Windows firewall entry." do
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,
@@ -207,9 +220,7 @@ class Chef
end
end
- action :delete do
- description "Delete an existing Windows firewall entry."
-
+ action :delete, description: "Delete an existing Windows firewall entry." do
if current_resource
converge_by("delete firewall rule #{new_resource.rule_name}") do
powershell_exec!("Remove-NetFirewallRule -Name '#{new_resource.rule_name}'")
@@ -230,7 +241,7 @@ class Chef
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 << " -RemoteAddress '#{new_resource.remote_address.join("', '")}'" 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
diff --git a/lib/chef/resource/windows_font.rb b/lib/chef/resource/windows_font.rb
index c9128aa4b0..2a8aaa3feb 100644
--- a/lib/chef/resource/windows_font.rb
+++ b/lib/chef/resource/windows_font.rb
@@ -43,13 +43,11 @@ class Chef
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."
+ coerce: proc { |x| /^.:.*/.match?(x) ? x.tr("\\", "/").gsub("//", "/") : x }
+ action :install, description: "Install a font to the system fonts directory." do
if font_exists?
- logger.trace("Not installing font: #{new_resource.font_name} as font already installed.")
+ logger.debug("Not installing font: #{new_resource.font_name} as font already installed.")
else
retrieve_cookbook_font
install_font
diff --git a/lib/chef/resource/windows_pagefile.rb b/lib/chef/resource/windows_pagefile.rb
index 4dfaae3be3..6fb63f2f3b 100644
--- a/lib/chef/resource/windows_pagefile.rb
+++ b/lib/chef/resource/windows_pagefile.rb
@@ -39,16 +39,26 @@ class Chef
```ruby
windows_pagefile 'Delete the pagefile' do
- path 'C:\pagefile.sys'
+ path 'C'
action :delete
end
```
+ **Switch to system managed pagefiles**:
+
+ ```ruby
+ windows_pagefile 'Change the pagefile to System Managed' do
+ path 'E:\'
+ system_managed true
+ action :set
+ end
+ ```
+
**Create a pagefile with an initial and maximum size**:
```ruby
- windows_pagefile 'create the pagefile' do
- path 'C:\pagefile.sys'
+ windows_pagefile 'create the pagefile with these sizes' do
+ path 'f:\'
initial_size 100
maximum_size 200
end
@@ -56,7 +66,7 @@ class Chef
DOC
property :path, String,
- coerce: proc { |x| x.tr("/", '\\') },
+ 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
@@ -64,8 +74,7 @@ class Chef
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
+ description: "Enable automatic management of pagefile initial and maximum size. Setting this to true ignores `initial_size` and `maximum_size` properties."
property :initial_size, Integer,
description: "Initial size of the pagefile in megabytes."
@@ -73,25 +82,26 @@ class Chef
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
+ action :set, description: "Configures the default pagefile, creating if it doesn't exist." do
automatic_managed = new_resource.automatic_managed
if automatic_managed
set_automatic_managed unless automatic_managed?
- else
+ elsif automatic_managed == false
unset_automatic_managed if automatic_managed?
+ else
+ pagefile = clarify_pagefile_name
+ initial_size = new_resource.initial_size
+ maximum_size = new_resource.maximum_size
+ system_managed = new_resource.system_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)
+ # the method below is designed to raise an exception if the drive you are trying to create a pagefile for doesn't exist.
+ # PowerShell will happily let you create a pagefile called h:\pagefile.sys even though you don't have an H:\ drive.
+
+ pagefile_drive_exist?(pagefile)
+ create(pagefile) unless exists?(pagefile)
+ if (initial_size && maximum_size) || system_managed
if system_managed
set_system_managed(pagefile) unless max_and_min_set?(pagefile, 0, 0)
else
@@ -103,23 +113,33 @@ class Chef
end
end
- action :delete do
- description "Deletes the specified pagefile."
-
- validate_name
- delete(new_resource.path) if exists?(new_resource.path)
+ action :delete, description: "Deletes the specified pagefile." do
+ pagefile = clarify_pagefile_name
+ delete(pagefile) if exists?(pagefile)
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)
+ # We are adding support for a number of possibilities for how users will express the drive and location they want the pagefile written to.
+ def clarify_pagefile_name
+ case new_resource.path
+ # user enters C, C:, C:\, C:\\
+ when /^[a-zA-Z]/
+ new_resource.path[0] + ":\\pagefile.sys"
+ # user enters C:\pagefile.sys OR c:\foo\bar\pagefile.sys as the path
+ when /^[a-zA-Z]:.*.sys/
+ new_resource.path
+ else
+ raise "#{new_resource.path} does not match the format DRIVE:\\path\\pagefile.sys for pagefiles. Example: C:\\pagefile.sys"
+ end
+ end
- raise "#{new_resource.path} does not match the format DRIVE:\\path\\file.sys for pagefiles. Example: C:\\pagefile.sys"
+ # raise an exception if the target drive location is invalid
+ def pagefile_drive_exist?(pagefile)
+ if ::Dir.exist?(pagefile[0] + ":\\") == false
+ raise "You are trying to create a pagefile on a drive that does not exist!"
+ end
end
# See if the pagefile exists
@@ -128,9 +148,11 @@ class Chef
# @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)
+ logger.trace("Checking if #{pagefile} exists by running: Get-CimInstance Win32_PagefileSetting | Where-Object { $_.name -eq $($pagefile)} ")
+ cmd = "$page_file_name = '#{pagefile}';"
+ cmd << "$pagefile = Get-CimInstance Win32_PagefileSetting | Where-Object { $_.name -eq $($page_file_name)};"
+ cmd << "if ([string]::IsNullOrEmpty($pagefile)) { return $false } else { return $true }"
+ powershell_exec!(cmd).result
end
end
@@ -141,11 +163,14 @@ class Chef
# @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
+ logger.trace("Checking if #{pagefile} has max and initial disk size values set")
+ cmd = "$page_file = '#{pagefile}';"
+ cmd << "$driveLetter = $page_file.split(':')[0];"
+ cmd << "$page_file_settings = Get-CimInstance -ClassName Win32_PageFileSetting -Filter \"SettingID='pagefile.sys @ $($driveLetter):'\" -Property * -ErrorAction Stop;"
+ cmd << "if ($page_file_settings.InitialSize -eq #{min} -and $page_file_settings.MaximumSize -eq #{max})"
+ cmd << "{ return $true }"
+ cmd << "else { return $false }"
+ powershell_exec!(cmd).result
end
# create a pagefile
@@ -153,9 +178,10 @@ class Chef
# @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)
+ logger.trace("Running New-CimInstance -ClassName Win32_PageFileSetting to create new pagefile : #{pagefile}")
+ powershell_exec! <<~ELM
+ New-CimInstance -ClassName Win32_PageFileSetting -Property @{Name = "#{pagefile}"}
+ ELM
end
end
@@ -164,9 +190,13 @@ class Chef
# @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)
+ logger.trace("Running Remove-CimInstance for pagefile : #{pagefile}")
+ powershell_exec! <<~EOL
+ $page_file = "#{pagefile}"
+ $driveLetter = $page_file.split(':')[0]
+ $PageFile = (Get-CimInstance -ClassName Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ $($driveLetter):'" -ErrorAction Stop)
+ $null = ($PageFile | Remove-CimInstance -ErrorAction SilentlyContinue)
+ EOL
end
end
@@ -176,26 +206,31 @@ class Chef
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)
+ cmd = "$sys = Get-CimInstance Win32_ComputerSystem -Property *;"
+ cmd << "return $sys.AutomaticManagedPagefile"
+ powershell_exec!(cmd).result
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)
+ converge_by("Set pagefile to Automatic Managed") do
+ logger.trace("Running Set-CimInstance -InputObject $sys -Property @{AutomaticManagedPagefile=$true} -PassThru")
+ powershell_exec! <<~EOH
+ $sys = Get-CimInstance Win32_ComputerSystem -Property *
+ Set-CimInstance -InputObject $sys -Property @{AutomaticManagedPagefile=$true} -PassThru
+ EOH
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)
+ converge_by("Turn off Automatically Managed on pagefiles") do
+ logger.trace("Running Set-CimInstance -InputObject $sys -Property @{AutomaticManagedPagefile=$false} -PassThru")
+ powershell_exec! <<~EOH
+ $sys = Get-CimInstance Win32_ComputerSystem -Property *
+ Set-CimInstance -InputObject $sys -Property @{AutomaticManagedPagefile=$false} -PassThru
+ EOH
end
end
@@ -206,9 +241,14 @@ class Chef
# @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)
+ logger.trace("Set-CimInstance -Property @{InitialSize = #{min} MaximumSize = #{max}")
+ powershell_exec! <<~EOD
+ $page_file = "#{pagefile}"
+ $driveLetter = $page_file.split(':')[0]
+ Get-CimInstance -ClassName Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ $($driveLetter):'" -ErrorAction Stop | Set-CimInstance -Property @{
+ InitialSize = #{min}
+ MaximumSize = #{max}}
+ EOD
end
end
@@ -217,21 +257,16 @@ class Chef
# @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)
+ logger.trace("Running ")
+ powershell_exec! <<~EOM
+ $page_file = "#{pagefile}"
+ $driveLetter = $page_file.split(':')[0]
+ Get-CimInstance -ClassName Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ $($driveLetter):'" -ErrorAction Stop | Set-CimInstance -Property @{
+ InitialSize = 0
+ MaximumSize = 0}
+ EOM
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
diff --git a/lib/chef/resource/windows_path.rb b/lib/chef/resource/windows_path.rb
index 870ffdef3f..625ac01fd0 100644
--- a/lib/chef/resource/windows_path.rb
+++ b/lib/chef/resource/windows_path.rb
@@ -64,7 +64,7 @@ class Chef
end
end
- action :add do
+ action :add, description: "Add an item to the system path." do
# The windows Env provider does not correctly expand variables in
# the PATH environment variable. Ruby expects these to be expanded.
#
@@ -72,11 +72,11 @@ class Chef
env "path" do
action :modify
delim ::File::PATH_SEPARATOR
- value path.tr("/", '\\')
+ value path.tr("/", "\\")
end
end
- action :remove do
+ action :remove, description: "Remove an item from the system path." do
# The windows Env provider does not correctly expand variables in
# the PATH environment variable. Ruby expects these to be expanded.
#
@@ -84,7 +84,7 @@ class Chef
env "path" do
action :delete
delim ::File::PATH_SEPARATOR
- value path.tr("/", '\\')
+ value path.tr("/", "\\")
end
end
end
diff --git a/lib/chef/resource/windows_printer.rb b/lib/chef/resource/windows_printer.rb
index dea15ba112..24d746b648 100644
--- a/lib/chef/resource/windows_printer.rb
+++ b/lib/chef/resource/windows_printer.rb
@@ -1,6 +1,7 @@
#
# Author:: Doug Ireton (<doug@1strategy.com>)
# Copyright:: 2012-2018, Nordstrom, 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.
@@ -21,6 +22,10 @@ require_relative "../resource"
class Chef
class Resource
+ # @todo
+ # 1. Allow updating the printer properties
+ # 2. Fail with a warning if the port can't be found and create_port is false
+ # 3. Fail with helpful messaging if the printer driver can't be installed
class WindowsPrinter < Chef::Resource
unified_mode true
@@ -28,7 +33,7 @@ class Chef
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."
+ description "Use the **windows_printer** resource to setup Windows printers. This resource will automatically install the driver specified in the `driver_name` property and will automatically create a printer port using either the `ipv4_address` property or the `port_name property."
introduced "14.0"
examples <<~DOC
**Create a printer**:
@@ -49,6 +54,23 @@ class Chef
action :delete
end
```
+
+ **Create a printer port and a printer that uses that port (new in 17.3)**
+
+ ```ruby
+ windows_printer_port '10.4.64.39' do
+ port_name 'My awesome printer port'
+ snmp_enabled true
+ port_protocol 2
+ end
+
+ windows_printer 'HP LaserJet 5th Floor' do
+ driver_name 'HP LaserJet 4100 Series PCL6'
+ port_name 'My awesome printer port'
+ ipv4_address '10.4.64.38'
+ create_port false
+ end
+ ```
DOC
property :device_id, String,
@@ -83,82 +105,79 @@ class Chef
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)
+ property :create_port, [TrueClass, FalseClass],
+ description: "Create a printer port for the printer. Set this to false and specify the `port_name` property if using the `windows_printer_port` resource to create the port instead.",
+ introduced: "17.3",
+ default: true, desired_state: false
- # @todo Set @current_resource printer properties from registry
- load_current_value do |desired|
- name desired.name
- end
+ property :port_name, String,
+ description: "The port name.",
+ default: lazy { |x| "IP_#{x.ipv4_address}" },
+ introduced: "17.3",
+ default_description: "The resource block name or the ipv4_address prepended with IP_."
- action :create do
- description "Create a new printer and a printer port if one doesn't already exist."
+ load_current_value do |new_resource|
+ printer_data = powershell_exec(%Q{Get-WmiObject -Class Win32_Printer -Filter "Name='#{new_resource.device_id}'"}).result
- if printer_exists?
- Chef::Log.info "#{@new_resource} already exists - nothing to do."
+ if printer_data.empty?
+ current_value_does_not_exist!
else
- converge_by("Create #{@new_resource}") do
- create_printer
+ device_id new_resource.device_id
+ comment printer_data["Comment"]
+ default printer_data["Default"]
+ location printer_data["Location"]
+ shared printer_data["Shared"]
+ share_name printer_data["ShareName"]
+ port_name printer_data["PortName"]
+
+ driver_data = powershell_exec(%Q{Get-PrinterDriver -Name="#{new_resource.driver_name}"}).result
+ unless driver_data.empty?
+ driver_name new_resource.driver_name
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
+ action :create, description: "Create a new printer and printer port, if one doesn't already." do
+ if current_resource
+ Chef::Log.info "#{@new_resource} already exists - nothing to do."
else
- Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
- end
- end
+ # Create the printer port first unless the property is set to false
+ if new_resource.create_port
+ windows_printer_port new_resource.port_name do
+ ipv4_address new_resource.ipv4_address
+ port_name new_resource.port_name
+ 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
+ converge_by("install driver #{new_resource.driver_name}") do
+ powershell_exec!("Add-PrinterDriver -Name '#{new_resource.driver_name}'")
+ 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}";
- }
+ converge_by("create #{@new_resource.device_id}") do
+ powershell_exec! <<-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 = "#{new_resource.port_name}";
+ Shared = "$#{new_resource.shared}";
+ ShareName = "#{new_resource.share_name}";
+ }
EOH
end
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
+ action :delete, description: "Delete an existing printer. Note that this resource does not delete the associated printer port." do
+ if current_resource
+ converge_by("Delete #{new_resource.device_id}") do
+ powershell_exec!("Remove-Printer -Name '#{new_resource.device_id}'")
end
+ else
+ Chef::Log.info "#{new_resource.device_id} doesn't exist - can't delete."
end
end
end
diff --git a/lib/chef/resource/windows_printer_port.rb b/lib/chef/resource/windows_printer_port.rb
index 2a4eaa09b3..636ae9dd54 100644
--- a/lib/chef/resource/windows_printer_port.rb
+++ b/lib/chef/resource/windows_printer_port.rb
@@ -1,6 +1,7 @@
#
# Author:: Doug Ireton <doug@1strategy.com>
# Copyright:: 2012-2018, Nordstrom, 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.
@@ -68,14 +69,17 @@ class Chef
}
property :port_name, String,
- description: "The port name."
+ description: "The port name.",
+ default: lazy { |x| "IP_#{x.ipv4_address}" },
+ default_description: "The resource block name or the ipv4_address prepended with IP_."
property :port_number, Integer,
- description: "The port number.",
+ description: "The TCP port number.",
default: 9100
property :port_description, String,
- description: "The description of the port."
+ desired_state: false,
+ deprecated: true
property :snmp_enabled, [TrueClass, FalseClass],
description: "Determines if SNMP is enabled on the port.",
@@ -86,79 +90,58 @@ class Chef
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)
+ load_current_value do |new_resource|
+ port_data = powershell_exec(%Q{Get-WmiObject -Class Win32_TCPIPPrinterPort -Filter "Name='#{new_resource.port_name}'"}).result
- # @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
+ if port_data.empty?
+ current_value_does_not_exist!
else
- Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
+ ipv4_address port_data["HostAddress"]
+ port_name port_data["Name"]
+ snmp_enabled port_data["SNMPEnabled"]
+ port_protocol port_data["Protocol"]
+ port_number port_data["PortNumber"]
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}";
- }
+ action :create, description: "Create or update the printer port." do
+ converge_if_changed do
+ if current_resource
+ # update the printer port using PowerShell
+ powershell_exec! <<-EOH
+ Get-WmiObject Win32_TCPIPPrinterPort -EnableAllPrivileges -filter "Name='#{new_resource.port_name}'" |
+ ForEach-Object{
+ $_.HostAddress='#{new_resource.ipv4_address}'
+ $_.PortNumber='#{new_resource.port_number}'
+ $_.Protocol='#{new_resource.port_protocol}'
+ $_.SNMPEnabled='$#{new_resource.snmp_enabled}'
+ $_.Put()
+ }
+ EOH
+ else
+ # create the printer port using PowerShell
+ powershell_exec! <<-EOH
+ Set-WmiInstance -class Win32_TCPIPPrinterPort `
+ -EnableAllPrivileges `
+ -Argument @{ HostAddress = "#{new_resource.ipv4_address}";
+ Name = "#{new_resource.port_name}";
+ 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}"
+ end
+ end
- 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
+ action :delete, description: "Delete an existing printer port." do
+ if current_resource
+ converge_by("delete port #{new_resource.port_name}") do
+ powershell_exec!("Remove-PrinterPort -Name #{new_resource.port_name}")
end
+ else
+ Chef::Log.info "#{new_resource.port_name} doesn't exist - can't delete."
end
end
end
diff --git a/lib/chef/resource/windows_security_policy.rb b/lib/chef/resource/windows_security_policy.rb
index 1b0a285197..5c683492a3 100644
--- a/lib/chef/resource/windows_security_policy.rb
+++ b/lib/chef/resource/windows_security_policy.rb
@@ -17,6 +17,7 @@
# limitations under the License.
require_relative "../resource"
+require "tempfile" unless defined?(Tempfile)
class Chef
class Resource
@@ -27,6 +28,7 @@ class Chef
# The valid policy_names options found here
# https://github.com/ChrisAWalker/cSecurityOptions under 'AccountSettings'
+ # This needs to be revisited - the list at the link above is non-exhaustive and is missing a couple of items
policy_names = %w{LockoutDuration
MaximumPasswordAge
MinimumPasswordAge
@@ -35,6 +37,8 @@ class Chef
PasswordHistorySize
LockoutBadCount
ResetLockoutCount
+ AuditPolicyChange
+ LockoutDuration
RequireLogonToChangePassword
ForceLogoffWhenHourExpire
NewAdministratorName
@@ -43,7 +47,7 @@ class Chef
LSAAnonymousNameLookup
EnableAdminAccount
EnableGuestAccount
- }
+ }
description "Use the **windows_security_policy** resource to set a security policy on the Microsoft Windows platform."
introduced "16.0"
@@ -82,7 +86,56 @@ class Chef
property :secvalue, String, required: true,
description: "Policy value to be set for policy name."
- load_current_value do |desired|
+ load_current_value do |new_resource|
+ current_state = load_security_options
+
+ if new_resource.secoption == "ResetLockoutCount"
+ if new_resource.secvalue.to_i > current_state["LockoutDuration"].to_i
+ raise Chef::Exceptions::ValidationFailed, "The \"ResetLockoutCount\" value cannot be greater than the value currently set for \"LockoutDuration\""
+ end
+ end
+ if (new_resource.secoption == "ResetLockoutCount" || new_resource.secoption == "LockoutDuration") && current_state["LockoutBadCount"] == "0"
+ raise Chef::Exceptions::ValidationFailed, "#{new_resource.secoption} cannot be set unless the \"LockoutBadCount\" security policy has been set to a non-zero value"
+ end
+
+ secvalue current_state[new_resource.secoption.to_s]
+ end
+
+ action :set, description: "Set the Windows security policy" 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.tr("/", "\\")
+ 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.tr("/", "\\")
+ 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
@@ -108,44 +161,7 @@ class Chef
LockoutBadCount = $security_options_hash.LockoutBadCount
})
CODE
- output = powershell_exec(powershell_code)
- current_value_does_not_exist! if output.result.empty?
- state = output.result
-
- if desired.secoption == "ResetLockoutCount" || desired.secoption == "LockoutDuration"
- if state["LockoutBadCount"] == "0"
- raise Chef::Exceptions::ValidationFailed.new "#{desired.secoption} cannot be set unless the \"LockoutBadCount\" security policy has been set to a non-zero value"
- else
- secvalue state[desired.secoption.to_s]
- end
- else
- secvalue state[desired.secoption.to_s]
- end
- end
-
- action :set do
- converge_if_changed :secvalue do
- security_option = new_resource.secoption
- security_value = new_resource.secvalue
-
- cmd = <<-EOH
- $security_option = "#{security_option}"
- C:\\Windows\\System32\\secedit /export /cfg $env:TEMP\\#{security_option}_Export.inf
- if ( ($security_option -match "NewGuestName") -Or ($security_option -match "NewAdministratorName") )
- {
- $#{security_option}_Remediation = (Get-Content $env:TEMP\\#{security_option}_Export.inf) | Foreach-Object { $_ -replace '#{security_option}\\s*=\\s*\\"\\w*\\"', '#{security_option} = "#{security_value}"' } | Set-Content $env:TEMP\\#{security_option}_Export.inf
- C:\\Windows\\System32\\secedit /configure /db $env:windir\\security\\new.sdb /cfg $env:TEMP\\#{security_option}_Export.inf /areas SECURITYPOLICY
- }
- else
- {
- $#{security_option}_Remediation = (Get-Content $env:TEMP\\#{security_option}_Export.inf) | Foreach-Object { $_ -replace "#{security_option}\\s*=\\s*\\d*", "#{security_option} = #{security_value}" } | Set-Content $env:TEMP\\#{security_option}_Export.inf
- C:\\Windows\\System32\\secedit /configure /db $env:windir\\security\\new.sdb /cfg $env:TEMP\\#{security_option}_Export.inf /areas SECURITYPOLICY
- }
- Remove-Item $env:TEMP\\#{security_option}_Export.inf -force
- EOH
-
- powershell_exec!(cmd)
- end
+ powershell_exec(powershell_code).result
end
end
end
diff --git a/lib/chef/resource/windows_share.rb b/lib/chef/resource/windows_share.rb
index fe1e976747..32b6b9f4a0 100644
--- a/lib/chef/resource/windows_share.rb
+++ b/lib/chef/resource/windows_share.rb
@@ -70,17 +70,17 @@ class Chef
# 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 }
+ default: [], 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 }
+ default: [], 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 }
+ default: [], 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],
@@ -118,11 +118,11 @@ class Chef
# 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|
+ load_current_value do |new_resource|
# 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"
+ share_state_cmd = "Get-SmbShare -Name '#{new_resource.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)
@@ -146,14 +146,14 @@ class Chef
encrypt_data results["EncryptData"]
# folder_enumeration_mode results['FolderEnumerationMode']
- perm_state_cmd = %{Get-SmbShareAccess -Name "#{desired.share_name}" | Select-Object AccountName,AccessControlType,AccessRight}
+ perm_state_cmd = %{Get-SmbShareAccess -Name "#{new_resource.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?
+ raise "Could not determine #{new_resource.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}")
@@ -192,9 +192,7 @@ class Chef
name
end
- action :create do
- description "Create and modify Windows shares."
-
+ action :create, description: "Create or modify a Windows share." do
# 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
@@ -218,9 +216,7 @@ class Chef
end
end
- action :delete do
- description "Delete an existing Windows share."
-
+ action :delete, description: "Delete an existing Windows share." do
if current_resource.nil?
Chef::Log.debug("#{new_resource.share_name} does not exist - nothing to do")
else
@@ -275,14 +271,11 @@ class Chef
# 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
+ @users_to_revoke ||= 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
# update existing permissions on a share
diff --git a/lib/chef/resource/windows_shortcut.rb b/lib/chef/resource/windows_shortcut.rb
index f2264445ba..d432f41bca 100644
--- a/lib/chef/resource/windows_shortcut.rb
+++ b/lib/chef/resource/windows_shortcut.rb
@@ -57,11 +57,11 @@ class Chef
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|
+ load_current_value do |new_resource|
require "win32ole" if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
- link = WIN32OLE.new("WScript.Shell").CreateShortcut(desired.shortcut_name)
- name desired.shortcut_name
+ link = WIN32OLE.new("WScript.Shell").CreateShortcut(new_resource.shortcut_name)
+ name new_resource.shortcut_name
target(link.TargetPath)
arguments(link.Arguments)
description(link.Description)
@@ -69,9 +69,7 @@ class Chef
iconlocation(link.IconLocation)
end
- action :create do
- description "Create or modify a Windows shortcut."
-
+ action :create, description: "Create or modify a Windows shortcut." do
converge_if_changed do
converge_by "creating shortcut #{new_resource.shortcut_name}" do
link = WIN32OLE.new("WScript.Shell").CreateShortcut(new_resource.shortcut_name)
diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb
index 29bade29ce..b1154a1f08 100644
--- a/lib/chef/resource/windows_task.rb
+++ b/lib/chef/resource/windows_task.rb
@@ -21,6 +21,7 @@ require_relative "../resource"
require_relative "../win32/security" if ChefUtils.windows_ruby?
autoload :ISO8601, "iso8601" if ChefUtils.windows_ruby?
require_relative "../util/path_helper"
+require_relative "../util/backup"
require "win32/taskscheduler" if ChefUtils.windows_ruby?
class Chef
@@ -48,7 +49,7 @@ class Chef
**Create a scheduled task to run every 2 days**:
- ``` ruby
+ ```ruby
windows_task 'chef-client' do
command 'chef-client'
run_level :highest
@@ -236,6 +237,10 @@ class Chef
introduced: "14.15", default: false,
description: "To start the task at any time after its scheduled time has passed."
+ property :backup, [Integer, FalseClass],
+ introduced: "17.0", default: 5,
+ description: "Number of backups to keep of the task when modified/deleted. Set to false to disable backups."
+
attr_accessor :exists, :task, :command_arguments
VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze
@@ -544,7 +549,7 @@ class Chef
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}"
+ 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
@@ -564,6 +569,7 @@ class Chef
def update_task(task)
converge_by("#{new_resource} task updated") do
+ do_backup
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
@@ -948,6 +954,11 @@ class Chef
def get_day(date)
Date.strptime(date, "%m/%d/%Y").strftime("%a").upcase
end
+
+ def do_backup
+ file = "C:/Windows/System32/Tasks/#{new_resource.task_name}"
+ Chef::Util::Backup.new(new_resource, file).backup!
+ end
end
action :create do
@@ -998,7 +1009,7 @@ class Chef
end
end
else
- logger.warn "#{new_resource} task does not exist - nothing to do"
+ logger.debug "#{new_resource} task does not exist - nothing to do"
end
end
@@ -1006,11 +1017,12 @@ class Chef
if current_resource.exists
logger.trace "#{new_resource} task exists"
converge_by("delete scheduled task #{new_resource}") do
+ do_backup
ts = ::Win32::TaskScheduler.new
ts.delete(current_resource.task_name)
end
else
- logger.warn "#{new_resource} task does not exist - nothing to do"
+ logger.debug "#{new_resource} task does not exist - nothing to do"
end
end
@@ -1018,14 +1030,14 @@ class Chef
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"
+ logger.debug "#{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"
+ logger.debug "#{new_resource} task does not exist - nothing to do"
end
end
@@ -1038,7 +1050,7 @@ class Chef
run_schtasks "CHANGE", "ENABLE" => ""
end
else
- logger.trace "#{new_resource} already enabled - nothing to do"
+ logger.debug "#{new_resource} already enabled - nothing to do"
end
else
logger.fatal "#{new_resource} task does not exist - nothing to do"
diff --git a/lib/chef/resource/windows_uac.rb b/lib/chef/resource/windows_uac.rb
index db5d5fd173..330a6432bc 100644
--- a/lib/chef/resource/windows_uac.rb
+++ b/lib/chef/resource/windows_uac.rb
@@ -29,7 +29,7 @@ class Chef
examples <<~DOC
**Disable UAC prompts for the admin**:
- ``` ruby
+ ```ruby
windows_uac 'Disable UAC prompts for the admin' do
enable_uac true
prompt_on_secure_desktop false
@@ -39,7 +39,7 @@ class Chef
**Disable UAC entirely**:
- ``` ruby
+ ```ruby
windows_uac 'Disable UAC entirely' do
enable_uac false
end
@@ -72,9 +72,7 @@ class Chef
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\''
-
+ action :configure, description: "Configures UAC by setting registry keys at `HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System`." do
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) },
diff --git a/lib/chef/resource/windows_user_privilege.rb b/lib/chef/resource/windows_user_privilege.rb
index 971338303d..36dd730f64 100644
--- a/lib/chef/resource/windows_user_privilege.rb
+++ b/lib/chef/resource/windows_user_privilege.rb
@@ -147,7 +147,7 @@ class Chef
end
end
- action :add do
+ action :add, description: "Add a user privilege." 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)
@@ -155,7 +155,7 @@ class Chef
end
end
- action :set do
+ action :set, description: "Set the privileges that are listed in the `privilege` property for only the users listed in the `users` property." do
if new_resource.users.nil? || new_resource.users.empty?
raise Chef::Exceptions::ValidationFailed, "Users are required property with set action."
end
@@ -190,7 +190,7 @@ class Chef
end
end
- action :clear do
+ action :clear, description: "Clear all user privileges" do
new_resource.privilege.each do |privilege|
accounts = Chef::ReservedNames::Win32::Security.get_account_with_user_rights(privilege)
@@ -204,7 +204,7 @@ class Chef
end
end
- action :remove do
+ action :remove, description: "Remove a user privilege" do
curr_res_privilege = current_resource.privilege
missing_res_privileges = (new_resource.privilege - curr_res_privilege)
diff --git a/lib/chef/resource/windows_workgroup.rb b/lib/chef/resource/windows_workgroup.rb
index 3c49f7cb3e..acb3bee542 100644
--- a/lib/chef/resource/windows_workgroup.rb
+++ b/lib/chef/resource/windows_workgroup.rb
@@ -30,13 +30,13 @@ class Chef
examples <<~DOC
**Join a workgroup**:
- ``` ruby
+ ```ruby
windows_workgroup 'myworkgroup'
```
**Join a workgroup using a specific user**:
- ``` ruby
+ ```ruby
windows_workgroup 'myworkgroup' do
user 'Administrator'
password 'passw0rd'
@@ -87,8 +87,7 @@ class Chef
property :sensitive, [TrueClass, FalseClass],
default: true, desired_state: false
- action :join do
- description "Update the workgroup."
+ action :join, description: "Update the workgroup." do
unless workgroup_member?
converge_by("join workstation workgroup #{new_resource.workgroup_name}") do
diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb
index f7c4517c6d..53e02bf609 100644
--- a/lib/chef/resource/yum_package.rb
+++ b/lib/chef/resource/yum_package.rb
@@ -35,32 +35,32 @@ class Chef
examples <<~DOC
**Install an exact version**:
- ``` ruby
+ ```ruby
yum_package 'netpbm = 10.35.58-8.el8'
```
**Install a minimum version**:
- ``` ruby
+ ```ruby
yum_package 'netpbm >= 10.35.58-8.el8'
```
**Install a minimum version using the default action**:
- ``` ruby
+ ```ruby
yum_package 'netpbm'
```
**Install a version without worrying about the exact release**:
- ``` ruby
+ ```ruby
yum_package 'netpbm-10.35*'
```
**To install a package**:
- ``` ruby
+ ```ruby
yum_package 'netpbm' do
action :install
end
@@ -68,13 +68,13 @@ class Chef
**To install a partial minimum version**:
- ``` ruby
+ ```ruby
yum_package 'netpbm >= 10'
```
**To install a specific architecture**:
- ``` ruby
+ ```ruby
yum_package 'netpbm' do
arch 'i386'
end
@@ -82,13 +82,13 @@ class Chef
or:
- ``` ruby
+ ```ruby
yum_package 'netpbm.x86_64'
```
**To install a specific version-release**
- ``` ruby
+ ```ruby
yum_package 'netpbm' do
version '10.35.58-8.el8'
end
@@ -101,7 +101,7 @@ class Chef
to dump the in-memory Yum cache, and then use the repository immediately
to ensure that the correct package is installed:
- ``` ruby
+ ```ruby
cookbook_file '/etc/yum.repos.d/custom.repo' do
source 'custom'
mode '0755'
diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb
index 94d1a284fc..b5ad2688eb 100644
--- a/lib/chef/resource/yum_repository.rb
+++ b/lib/chef/resource/yum_repository.rb
@@ -50,6 +50,11 @@ class Chef
# 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."
diff --git a/lib/chef/resource/zypper_package.rb b/lib/chef/resource/zypper_package.rb
index 5901090abd..0c4fa47a88 100644
--- a/lib/chef/resource/zypper_package.rb
+++ b/lib/chef/resource/zypper_package.rb
@@ -30,7 +30,7 @@ class Chef
examples <<~DOC
**Install a package using package manager:**
- ``` ruby
+ ```ruby
zypper_package 'name of package' do
action :install
end
@@ -38,7 +38,7 @@ class Chef
**Install a package using local file:**
- ``` ruby
+ ```ruby
zypper_package 'jwhois' do
action :install
source '/path/to/jwhois.rpm'
@@ -47,10 +47,10 @@ class Chef
**Install without using recommend packages as a dependency:**
- ``` ruby
+ ```ruby
package 'apache2' do
options '--no-recommends'
- end
+ end
```
DOC
diff --git a/lib/chef/resource/zypper_repository.rb b/lib/chef/resource/zypper_repository.rb
index 05856cc9bc..228329d2ea 100644
--- a/lib/chef/resource/zypper_repository.rb
+++ b/lib/chef/resource/zypper_repository.rb
@@ -24,21 +24,37 @@ class Chef
unified_mode true
provides(:zypper_repository) { true }
- provides(:zypper_repo) { true }
+ provides(:zypper_repo) { true } # legacy cookbook compatibility
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
+ ```ruby
zypper_repository 'apache' do
baseurl 'http://download.opensuse.org/repositories/Apache'
- path '/openSUSE_Leap_15.0'
- type 'rpm-md'
+ path '/openSUSE_Leap_15.2'
+ type 'rpm-md'
priority '100'
end
```
+
+ **Remove the repo named 'apache'**:
+
+ ```ruby
+ zypper_repository 'apache' do
+ action :delete
+ end
+ ```
+
+ **Refresh the repo named 'apache'**:
+
+ ```ruby
+ zypper_repository 'apache' do
+ action :refresh
+ end
+ ```
DOC
property :repo_name, String,
@@ -66,8 +82,10 @@ class Chef
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 :gpgkey, [String, Array],
+ description: "The location of the repository key(s) to be imported.",
+ coerce: proc { |v| Array(v) },
+ default: []
property :baseurl, String,
description: "The base URL for the Zypper repository, such as `http://download.opensuse.org`."
@@ -95,10 +113,12 @@ class Chef
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."
+ description: "The name of the template for the repository file. Only necessary if you're using a custom template for the repository file."
property :cookbook, String,
- description: "The cookbook to source the repository template file from. Only necessary if you're not using the built in template.",
+ description: "The cookbook to source the repository template file from. Only necessary if you're using a custom template for the repository file.",
+ default: lazy { cookbook_name },
+ default_description: "The cookbook containing the resource",
desired_state: false
property :gpgautoimportkeys, [TrueClass, FalseClass],
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index 9f2cd657f9..13dc39ad2a 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -29,9 +29,10 @@ class Chef
attr_reader :recipe_name
attr_reader :enclosing_provider
attr_reader :resource
+ attr_reader :new_resource
# FIXME (ruby-2.1 syntax): most of these are mandatory
- def initialize(type: nil, name: nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
+ def initialize(type: nil, name: nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil, new_resource: nil)
@type = type
@name = name
@created_at = created_at
@@ -40,6 +41,7 @@ class Chef
@cookbook_name = cookbook_name
@recipe_name = recipe_name
@enclosing_provider = enclosing_provider
+ @new_resource = new_resource
end
def build(&block)
@@ -64,7 +66,11 @@ class Chef
if block_given?
resource.resource_initializing = true
begin
- resource.instance_eval(&block)
+ if new_resource.nil?
+ resource.instance_exec(&block)
+ else
+ resource.instance_exec(new_resource, &block)
+ end
ensure
resource.resource_initializing = false
end
diff --git a/lib/chef/resource_collection/resource_set.rb b/lib/chef/resource_collection/resource_set.rb
index db298afb63..26521010bd 100644
--- a/lib/chef/resource_collection/resource_set.rb
+++ b/lib/chef/resource_collection/resource_set.rb
@@ -131,7 +131,7 @@ class Chef
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"
+ "Use a String like `resource_type[resource_name]' or a Chef::Resource object"
end
end
diff --git a/lib/chef/resource_inspector.rb b/lib/chef/resource_inspector.rb
index 6d320f4202..95ae110170 100644
--- a/lib/chef/resource_inspector.rb
+++ b/lib/chef/resource_inspector.rb
@@ -41,7 +41,11 @@ class Chef
data[:description] = resource.description
# data[:deprecated] = resource.deprecated || false
data[:default_action] = resource.default_action
- data[:actions] = resource.allowed_actions
+ data[:actions] = {}
+ resource.allowed_actions.each do |action|
+ data[:actions][action] = resource.action_description(action)
+ end
+
data[:examples] = resource.examples
data[:introduced] = resource.introduced
data[:preview] = resource.preview_resource
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 4051ac2f49..c6a7be133f 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -135,7 +135,6 @@ class Chef
def action_collection_registration(action_collection)
@action_collection = action_collection
- action_collection.register(self) if reporting_enabled?
end
def post_reporting_data
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 843d5610b8..3fabc18920 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -65,6 +65,7 @@ require_relative "resource/homebrew_package"
require_relative "resource/homebrew_tap"
require_relative "resource/homebrew_update"
require_relative "resource/ifconfig"
+require_relative "resource/inspec_waiver_file_entry"
require_relative "resource/kernel_module"
require_relative "resource/ksh"
require_relative "resource/launchd"
@@ -125,7 +126,6 @@ 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"
@@ -148,6 +148,8 @@ 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_defender"
+require_relative "resource/windows_defender_exclusion"
require_relative "resource/windows_dfs_folder"
require_relative "resource/windows_dfs_namespace"
require_relative "resource/windows_dfs_server"
@@ -170,4 +172,4 @@ 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
+require_relative "resource/windows_security_policy"
diff --git a/lib/chef/run_lock.rb b/lib/chef/run_lock.rb
index 1f83b7ea5a..8382983ca0 100644
--- a/lib/chef/run_lock.rb
+++ b/lib/chef/run_lock.rb
@@ -173,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.tr('\\', "/").downcase}")
+ @mutex = Chef::ReservedNames::Win32::Mutex.new("Global\\#{runlock_file.tr("\\", "/").downcase}")
mutex.test
end
diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb
index 4405843a9b..089ab0627a 100644
--- a/lib/chef/runner.rb
+++ b/lib/chef/runner.rb
@@ -70,7 +70,7 @@ class Chef
end
end
- # Actually run the action for releases
+ # Run the action on the resource.
resource.run_action(action, notification_type, notifying_resource)
# Execute any immediate and queue up any delayed notifications
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index a425129fa8..89456e86b5 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -25,6 +25,7 @@ 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 "chef-config/mixin/dot_d"
require_relative "../chef"
require_relative "version"
@@ -211,6 +212,7 @@ module Shell
class Options
include Mixlib::CLI
+ include ChefConfig::Mixin::DotD
def self.footer(text = nil)
@footer = text if text
@@ -341,15 +343,44 @@ module Shell
# We have to nuke ARGV to make sure irb's option parser never sees it.
# otherwise, IRB complains about command line switches it doesn't recognize.
ARGV.clear
+
+ # This code should not exist.
+ # We should be using Application::Client and then calling load_config_file
+ # which does all this properly. However this will do for now.
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.exist?(config[:config_file]) && File.readable?(config[:config_file])
+
+ # load the config (if we have one)
+ unless config[:config_file].nil?
+ if File.exist?(config[:config_file]) && File.readable?(config[:config_file])
+ Chef::Config.from_file(config[:config_file])
+ end
+
+ # even if we couldn't load that, we need to tell Chef::Config what
+ # the file was so it sets conf dir and d_dir and such properly
+ Chef::Config[:config_file] = config[:config_file]
+
+ # now attempt to load any relevant dot-dirs
+ load_dot_d(Chef::Config[:client_d_dir]) if Chef::Config[:client_d_dir]
+ end
+
+ # finally merge command-line options in
Chef::Config.merge!(config)
end
private
+ # shamelessly lifted from application.rb
+ def apply_config(config_content, config_file_path)
+ Chef::Config.from_string(config_content, config_file_path)
+ rescue Exception => error
+ logger.fatal("Configuration error #{error.class}: #{error.message}")
+ filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
+ filtered_trace.each { |line| logger.fatal(" " + line ) }
+ raise Chef::Exceptions::ConfigurationError.new("Aborting due to error in '#{config_file_path}': #{error}")
+ end
+
def config_file_for_shell_mode(environment)
dot_chef_dir = Chef::Util::PathHelper.home(".chef")
if config[:config_file]
diff --git a/lib/chef/shell/ext.rb b/lib/chef/shell/ext.rb
index b884658e98..e23a4aac42 100644
--- a/lib/chef/shell/ext.rb
+++ b/lib/chef/shell/ext.rb
@@ -198,9 +198,9 @@ module Shell
prints a detailed explanation of the command if available, or the
description if no explanation is available.
E
- def help(commmand = nil)
- if commmand
- explain_command(commmand)
+ def help(command = nil)
+ if command
+ explain_command(command)
else
puts help_banner
end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index e578cc2131..4ebcb3a463 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -36,7 +36,6 @@ require_relative "server_api"
# should be removed once client support for Open Source Chef Server 11 expires.
class Chef
class User
-
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb
index 945f0197df..3720f5efe7 100644
--- a/lib/chef/user_v1.rb
+++ b/lib/chef/user_v1.rb
@@ -145,11 +145,11 @@ class Chef
payload = {
username: @username,
display_name: @display_name,
- first_name: @first_name,
- last_name: @last_name,
email: @email,
- password: @password,
}
+ payload[:first_name] = @first_name unless @first_name.nil?
+ payload[:last_name] = @last_name unless @last_name.nil?
+ payload[:password] = @password unless @password.nil?
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?
@@ -258,7 +258,6 @@ class Chef
end
# Class Methods
-
def self.from_hash(user_hash)
user = Chef::UserV1.new
user.username user_hash["username"]
diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb
index 7e78e1ecab..8ca6249a9d 100644
--- a/lib/chef/util/dsc/configuration_generator.rb
+++ b/lib/chef/util/dsc/configuration_generator.rb
@@ -88,6 +88,7 @@ class Chef::Util::DSC
when FalseClass
switch_present = false
when TrueClass
+ # nothing
when String
switch_argument = escape_string_parameter_value(switch_value)
else
@@ -105,7 +106,7 @@ class Chef::Util::DSC
# The name may not be null or empty, and should start with a letter.
def validate_configuration_name!(configuration_name)
if !!(configuration_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
- raise ArgumentError, 'Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name'
+ raise ArgumentError, "Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name"
end
end
diff --git a/lib/chef/util/dsc/local_configuration_manager.rb b/lib/chef/util/dsc/local_configuration_manager.rb
index 091d4aa426..c0f9c72da8 100644
--- a/lib/chef/util/dsc/local_configuration_manager.rb
+++ b/lib/chef/util/dsc/local_configuration_manager.rb
@@ -103,7 +103,7 @@ class Chef::Util::DSC
end
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)
+ !! (dsc_exception_output.gsub(/\n+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
end
def dsc_module_import_failure?(command_output)
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index 2ed9051e3d..045900bec4 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -23,7 +23,7 @@ require_relative "version_string"
class Chef
CHEF_ROOT = File.expand_path("..", __dir__)
- VERSION = Chef::VersionString.new("16.7.68")
+ VERSION = Chef::VersionString.new("17.3.0")
end
#
diff --git a/lib/chef/version_string.rb b/lib/chef/version_string.rb
index 8da5df570a..c98d4c9a75 100644
--- a/lib/chef/version_string.rb
+++ b/lib/chef/version_string.rb
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef-utils/version_string"
+require "chef-utils/version_string" unless defined?(ChefUtils::VersionString)
class Chef
VersionString = ChefUtils::VersionString
diff --git a/lib/chef/win32/api.rb b/lib/chef/win32/api.rb
index 957823220d..893c819da3 100644
--- a/lib/chef/win32/api.rb
+++ b/lib/chef/win32/api.rb
@@ -43,6 +43,8 @@ class Chef
host.ffi_convention :stdcall
+ win64 = ENV["PROCESSOR_ARCHITECTURE"] == "AMD64"
+
# Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
host.typedef :ushort, :ATOM # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
# places a string in an atom table and receives a 16-bit integer, called an atom, that
@@ -120,10 +122,15 @@ class Chef
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;
- host.typedef :long, :LPARAM # Message parameter. WinDef.h as follows: #host.typedef LONG_PTR LPARAM;
+ if win64
+ host.typedef :int64, :LONG_PTR # Signed long type for pointer precision. Use when casting a pointer to a long to
+ host.typedef :int64, :LPARAM # Message parameter. WinDef.h as follows: #host.typedef LONG_PTR LPARAM;
+ else
+ host.typedef :long, :LONG_PTR # Signed long type for pointer precision. Use when casting a pointer to a long to
+ host.typedef :long, :LPARAM # Message parameter. WinDef.h as follows: #host.typedef LONG_PTR LPARAM;
+ end
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;
host.typedef :pointer, :LPCOLORREF # Pointer to a COLORREF value. WinDef.h as follows: #host.typedef DWORD *LPCOLORREF;
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 4b5f8ede41..4f7f2b2d52 100644
--- a/lib/chef/win32/registry.rb
+++ b/lib/chef/win32/registry.rb
@@ -21,9 +21,11 @@ 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"
+ module Win32
+ autoload :Registry, File.expand_path("../monkey_patches/win32/registry", __dir__)
+ end
+ require_relative "api/registry"
end
class Chef