diff options
author | Bryan McLellan <btm@loftninjas.org> | 2019-05-06 12:42:28 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-06 12:42:28 -0400 |
commit | 4e9dddc3a7667c22a5f76217f2ea6195574cccba (patch) | |
tree | f090f616249128cf3a325c8d79b646a1775118c6 | |
parent | 3f8efb05853364bcff846e9f98bb6fbf6773fe58 (diff) | |
parent | a1c438aa2b15b310ecccd062ae714026f39d3f12 (diff) | |
download | chef-4e9dddc3a7667c22a5f76217f2ea6195574cccba.tar.gz |
Merge pull request #8442 from chef/mp/bootstrap/restore-prerelease
Restore bootstrap pre-release support
-rw-r--r-- | RELEASE_NOTES.md | 4 | ||||
-rw-r--r-- | lib/chef/knife.rb | 25 | ||||
-rw-r--r-- | lib/chef/knife/bootstrap.rb | 85 | ||||
-rw-r--r-- | lib/chef/knife/bootstrap/templates/chef-full.erb | 4 | ||||
-rw-r--r-- | lib/chef/knife/core/bootstrap_context.rb | 21 | ||||
-rw-r--r-- | lib/chef/knife/core/windows_bootstrap_context.rb | 18 | ||||
-rw-r--r-- | spec/unit/knife/bootstrap_spec.rb | 22 | ||||
-rw-r--r-- | spec/unit/knife/core/bootstrap_context_spec.rb | 43 | ||||
-rw-r--r-- | spec/unit/knife/core/windows_bootstrap_context_spec.rb | 25 | ||||
-rw-r--r-- | spec/unit/knife_spec.rb | 21 |
10 files changed, 137 insertions, 131 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b0913a2a2f..ea1871d677 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -114,10 +114,11 @@ Using removed options will cause the command to fail. | --ssh-port | --connection-port | `knife[:ssh_port]` config setting remains available. | --ssh-user | --connection-user | `knife[:ssh_user]` config setting remains available. | --ssl-peer-fingerprint | --winrm-ssl-peer-fingerprint | | +| --prerelease |--channel CHANNEL | This now allows you to specify the channel that Chef Infra Client gets installed from. Valid values are _stable_, current, and unstable. 'current' has the same effect as using the old --prerelease. | | --winrm-authentication-protocol=PROTO | --winrm-auth-method=AUTH-METHOD | Valid values: plaintext, kerberos, ssl, _negotiate_| | --winrm-password| --connection-password | | | --winrm-port| --connection-port | `knife[:winrm_port]` config setting remains available.| -| --winrm-ssl-verify-mode MODE | --winrm-no-verify-cert | [1] Mode is not accepted. When flag is present, SSL cert will not be verified. Same as original mode of 'verify_none'. | +| --winrm-ssl-verify-mode MODE | --winrm-no-verify-cert | [1] Mode is not accepted. When flag is present, SSL cert will not be verified. Same as original mode of 'verify\_none'. | | --winrm-transport TRANSPORT | --winrm-ssl | [1] Use this flag if the target host is accepts WinRM connections over SSL. | --winrm-user | --connection-user | `knife[:winrm_user]` config setting remains available.| @@ -131,7 +132,6 @@ Using removed options will cause the command to fail. |--kerberos-keytab-file| This option existed but was not implemented.| |--winrm-codepage| This was used under knife-windows because bootstrapping was performed over a `cmd` shell. It is now invoked from `powershell`, so this option is no longer used.| |--winrm-shell|This option was ignored for bootstrap.| -|--prerelease|Chef now releases all development builds to our current channel and does not perform pre-release gem releases.| |--install-as-service|Installing Chef client as a service is not supported| #### Usage Changes diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb index da9c1ab1e9..4cd7cdb73c 100644 --- a/lib/chef/knife.rb +++ b/lib/chef/knife.rb @@ -344,13 +344,8 @@ class Chef # Chef::Config[:knife] would break the defaults in the cli that we would otherwise # overwrite. def config_file_settings - @key_sources = { cli: [], config: [] } cli_keys.each_with_object({}) do |key, memo| - if config.key?(key) - @key_sources[:cli] << key - end if Chef::Config[:knife].key?(key) - @key_sources[:config] << key memo[key] = Chef::Config[:knife][key] end end @@ -361,16 +356,28 @@ class Chef # config_file_settings - Chef::Config[:knife] sub-hash # config - mixlib-cli settings (accessor from the mixin) def merge_configs + # This is the config after user CLI options have been evaluated, and it contains only + # user-supplied values. + @original_config = config.dup # other code may have a handle to the config object, so use Hash#replace to deliberately # update-in-place. config.replace(default_config.merge(config_file_settings).merge(config)) end - # Return where a config key has been sourced, - # :cli, :config, or nil if the key is not set. + # + # 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] + # - :cli_default - came from a declared CLI `option`'s `default` value. + # - nil - if they key does not exist def config_source(key) - return :cli if @key_sources[:cli].include? key - return :config if @key_sources[:config].include? key + return :cli if @original_config.include? key + return :config if config_file_settings.key? key + return :cli_default if default_config.include? key nil end diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb index 69d9ffb8c0..5d76736e06 100644 --- a/lib/chef/knife/bootstrap.rb +++ b/lib/chef/knife/bootstrap.rb @@ -86,7 +86,8 @@ class Chef short: "-w AUTH-METHOD", long: "--winrm-auth-method AUTH-METHOD", description: "The WinRM authentication method to use. Valid choices are #{friendly_opt_list(WINRM_AUTH_PROTOCOL_LIST)}.", - proc: Proc.new { |protocol| Chef::Config[:knife][:winrm_auth_method] = protocol } + proc: Proc.new { |protocol| Chef::Config[:knife][:winrm_auth_method] = protocol }, + in: WINRM_AUTH_PROTOCOL_LIST option :winrm_basic_auth_only, long: "--winrm-basic-auth-only", @@ -156,6 +157,12 @@ class Chef description: "The version of #{Chef::Dist::PRODUCT} to install.", proc: lambda { |v| Chef::Config[:knife][:bootstrap_version] = v } + option :channel, + long: "--channel CHANNEL", + description: "Install from the given channel. Valid values are 'stable, 'current', and 'unstable'. 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", @@ -347,42 +354,45 @@ class Chef DEPRECATED_FLAGS = { # deprecated_key: [new_key, deprecated_long] - auth_timeout: [:max_wait, "--max-wait SECONDS"], - host_key_verify: [:ssh_verify_host_key, - "--[no-]host-key-verify", - ], - ssh_user: [:connection_user, - "--ssh-user USER", - ], - ssh_password: [:connection_password, - "--ssh-password PASSWORD", - ], - ssh_port: [:connection_port, - "-ssh-port", - ], - ssl_peer_fingerprint: [:winrm_ssl_peer_fingerprint, - "--ssl-peer-fingerprint FINGERPRINT", - ], - winrm_user: [:connection_user, - "--winrm-user USER", - ], - winrm_password: [:connection_password, - "--winrm-password", - ], - winrm_port: [:connection_port, - "--winrm-port", - ], - winrm_authentication_protocol: [:winrm_auth_method, - "--winrm-authentication-protocol PROTOCOL", - ], + # optional third element: replacement_value - if converting from bool + # (--bool-option) to valued flag (--new-option VALUE) + # this will be the value that is assigned the new flag when the old flag is used. + auth_timeout: [:max_wait, "--max-wait SECONDS" ], + host_key_verify: + [:ssh_verify_host_key, "--[no-]host-key-verify"], + prerelease: + [:channel, "--prerelease", "current"], + ssh_user: + [:connection_user, "--ssh-user USER"], + ssh_password: + [:connection_password, "--ssh-password PASSWORD"], + ssh_port: + [:connection_port, "-ssh-port" ], + ssl_peer_fingerprint: + [:winrm_ssl_peer_fingerprint, "--ssl-peer-fingerprint FINGERPRINT"], + winrm_user: + [:connection_user, "--winrm-user USER"], + winrm_password: + [:connection_password, "--winrm-password"], + winrm_port: + [:connection_port, "--winrm-port"], + winrm_authentication_protocol: + [:winrm_auth_method, "--winrm-authentication-protocol PROTOCOL"], }.freeze DEPRECATED_FLAGS.each do |deprecated_key, deprecation_entry| - new_key, deprecated_long = deprecation_entry + new_key, deprecated_long, replacement_value = deprecation_entry new_long = options[new_key][:long] + new_long_desc = if replacement_value.nil? + new_long + else + "#{new_long.split(" ").first} #{replacement_value}" + end option(deprecated_key, long: deprecated_long, - description: "#{deprecated_long} is deprecated. Use #{new_long} instead.", - boolean: options[new_key][:boolean]) + description: "This flag is deprecated. Please use '#{new_long_desc}' instead.", + boolean: options[new_key][:boolean] || !replacement_value.nil?, + # Put deprecated options at the end of the options list + on: :tail) end attr_accessor :client_builder @@ -638,12 +648,13 @@ class Chef end # If any deprecated flags are used, let the user know and - # update config[new-key] to the value given to the deprecated flag. + # update config[new-key] to the value given to the deprecated flag, + # or to the mapped value in case of changing flag type. # If a deprecated flag and its corresponding replacement - # are both used, raise an error. + # are both used, exit def verify_deprecated_flags! DEPRECATED_FLAGS.each do |deprecated_key, deprecation_entry| - new_key, deprecated_long = deprecation_entry + new_key, deprecated_long, replacement_value = deprecation_entry if config.key?(deprecated_key) && config_source(deprecated_key) == :cli if config.key?(new_key) && config_source(new_key) == :cli new_long = options[new_key][:long].split(" ").first @@ -656,9 +667,9 @@ class Chef EOM exit 1 else - config[new_key] = config[deprecated_key] + config[new_key] = replacement_value || config[deprecated_key] unless Chef::Config[:silence_deprecation_warnings] == true - ui.warn options[deprecated_key][:description] + ui.warn "You provided #{deprecated_long.split(" ").first}. #{options[deprecated_key][:description]}" end end end diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb index 58a64a23b8..54fa3a61d1 100644 --- a/lib/chef/knife/bootstrap/templates/chef-full.erb +++ b/lib/chef/knife/bootstrap/templates/chef-full.erb @@ -175,9 +175,9 @@ do_download() { if test -f /usr/bin/<%= Chef::Dist::CLIENT %>}; then echo "-----> Existing <%= Chef::Dist::PRODUCT %> installation detected" else - echo "-----> Installing Chef Omnibus (<%= latest_current_chef_version_string %>)" + echo "-----> Installing Chef Omnibus (<%= @config[:channel] %>/<%= version_to_install %>)" do_download ${install_sh} $tmp_dir/install.sh - sh $tmp_dir/install.sh -P chef <%= latest_current_chef_version_string %> + sh $tmp_dir/install.sh -P chef -c <%= @config[:channel] %> -v <%= version_to_install %> fi <% end %> diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index dcca7b8a69..5a27836da8 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -187,16 +187,17 @@ class Chef end # - # chef version string to fetch the latest current version from omnitruck - # If user is on X.Y.Z, bootstrap will use the latest X release - def latest_current_chef_version_string - chef_version_string = if knife_config[:bootstrap_version] - knife_config[:bootstrap_version] - else - Chef::VERSION.split(".").first - end - - "-v #{chef_version_string}" + # Returns the version of Chef to install (as recognized by the Omnitruck API) + # + # @return [String] download version string + def version_to_install + return knife_config[:bootstrap_version] if knife_config[:bootstrap_version] + + if @config[:channel] == "stable" + Chef::VERSION.split(".").first + else + "latest" + end end def first_boot diff --git a/lib/chef/knife/core/windows_bootstrap_context.rb b/lib/chef/knife/core/windows_bootstrap_context.rb index 6054743106..109f8e6f37 100644 --- a/lib/chef/knife/core/windows_bootstrap_context.rb +++ b/lib/chef/knife/core/windows_bootstrap_context.rb @@ -158,16 +158,6 @@ class Chef start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n" end - def latest_current_windows_chef_version_query - chef_version_string = if knife_config[:bootstrap_version] - knife_config[:bootstrap_version] - else - Chef::VERSION.split(".").first - end - - "&v=#{chef_version_string}" - 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. @@ -273,16 +263,16 @@ class Chef "%TEMP%\\#{Chef::Dist::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) - # The default msi path has a number of url query parameters - we attempt to substitute - # such parameters in as long as they are provided by the template. - 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 += latest_current_windows_chef_version_query + url += "&channel=#{@config[:channel]}" + url += "&v=#{version_to_install}" else @config[:msi_url] end diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb index 995a2ef4c9..4261a3a166 100644 --- a/spec/unit/knife/bootstrap_spec.rb +++ b/spec/unit/knife/bootstrap_spec.rb @@ -1603,29 +1603,31 @@ describe Chef::Knife::Bootstrap do end context "when a deprecated CLI flag is given on the CLI" do - before do - knife.config[:ssh_user] = "sshuser" - knife.merge_configs - end + let(:bootstrap_cli_options) { %w{--ssh-user sshuser} } it "maps the key value to the new key and points the human to the new flag" do - expect(knife.ui).to receive(:warn).with(/--ssh-user USER is deprecated. Use --connection-user USERNAME instead./) + expect(knife.ui).to receive(:warn).with(/You provided --ssh-user. This flag is deprecated. Please use '--connection-user USERNAME' instead./) knife.verify_deprecated_flags! expect(knife.config[:connection_user]).to eq "sshuser" end end context "when a deprecated CLI flag is given on the CLI, along with its replacement" do - before do - knife.config[:ssh_user] = "sshuser" - knife.config[:connection_user] = "real-user" - knife.merge_configs - end + let(:bootstrap_cli_options) { %w{--connection-user a --ssh-user b} } it "informs the human that both are provided and exits" do expect(knife.ui).to receive(:error).with(/You provided both --connection-user and --ssh-user.*Please use.*/m) expect { knife.verify_deprecated_flags! }.to raise_error SystemExit end end + + context "when a deprecated boolean CLI flag is given on the CLI, and its non-boolean replacement is used" do + let(:bootstrap_cli_options) { %w{--prerelease} } + it "correctly maps the old boolean value to the new value" do + expect(knife.ui).to receive(:warn) + knife.verify_deprecated_flags! + expect(knife.config[:channel]).to eq "current" + end + end end describe "#register_client" do diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb index 2ed8b6bc51..d57e254793 100644 --- a/spec/unit/knife/core/bootstrap_context_spec.rb +++ b/spec/unit/knife/core/bootstrap_context_spec.rb @@ -156,25 +156,6 @@ describe Chef::Knife::Core::BootstrapContext do end end - describe "when a bootstrap_version is specified" do - let(:chef_config) do - { - knife: { bootstrap_version: "11.12.4" }, - } - end - - it "should send the full version to the installer" do - expect(bootstrap_context.latest_current_chef_version_string).to eq("-v 11.12.4") - end - end - - describe "when a bootstrap_version is not specified" do - it "should send the latest current to the installer" do - # Intentionally hard coded in order not to replicate the logic. - expect(bootstrap_context.latest_current_chef_version_string).to eq("-v #{Chef::VERSION.to_i}") - end - end - describe "ssl_verify_mode" do it "isn't set in the config_content by default" do expect(bootstrap_context.config_content).not_to include("ssl_verify_mode") @@ -292,4 +273,28 @@ describe Chef::Knife::Core::BootstrapContext do end end + + describe "#version_to_install" do + context "when bootstrap_version is provided" do + let(:chef_config) { { knife: { bootstrap_version: "awesome" } } } + + it "returns bootstrap_version" do + expect(bootstrap_context.version_to_install).to eq "awesome" + end + end + + context "when bootstrap_version is not provided" do + let(:config) { { channel: "stable" } } + it "returns the currently running major version out of Chef::VERSION" do + expect(bootstrap_context.version_to_install).to eq Chef::VERSION.split(".").first + end + end + + context "and channel is other than stable" do + let(:config) { { channel: "unstable" } } + it "returns the version string 'latest'" do + expect(bootstrap_context.version_to_install).to eq "latest" + end + end + end end diff --git a/spec/unit/knife/core/windows_bootstrap_context_spec.rb b/spec/unit/knife/core/windows_bootstrap_context_spec.rb index a19ad11247..7bc73c113a 100644 --- a/spec/unit/knife/core/windows_bootstrap_context_spec.rb +++ b/spec/unit/knife/core/windows_bootstrap_context_spec.rb @@ -176,29 +176,30 @@ describe Chef::Knife::Core::WindowsBootstrapContext do end end - describe "latest_current_windows_chef_version_query" do - it "returns the major version of the current version of Chef" do - stub_const("Chef::VERSION", "15.1.2") - expect(bootstrap_context.latest_current_windows_chef_version_query).to eq("&v=15") - end - - end - describe "msi_url" do - context "when config option is not set" do + context "when msi_url config option is not set" do + let(:config) { { channel: "stable" } } before do - expect(bootstrap_context).to receive(:latest_current_windows_chef_version_query).and_return("&v=something") + expect(bootstrap_context).to receive(:version_to_install).and_return("something") end it "returns a chef.io msi url with minimal url parameters" do - reference_url = "https://www.chef.io/chef/download?p=windows&v=something" + reference_url = "https://www.chef.io/chef/download?p=windows&channel=stable&v=something" expect(bootstrap_context.msi_url).to eq(reference_url) end it "returns a chef.io msi url with provided url parameters substituted" do - reference_url = "https://www.chef.io/chef/download?p=windows&pv=machine&m=arch&DownloadContext=ctx&v=something" + reference_url = "https://www.chef.io/chef/download?p=windows&pv=machine&m=arch&DownloadContext=ctx&channel=stable&v=something" expect(bootstrap_context.msi_url("machine", "arch", "ctx")).to eq(reference_url) end + + context "when a channel is provided in config" do + let(:config) { { channel: "current" } } + it "returns a chef.io msi url with the requested channel" do + reference_url = "https://www.chef.io/chef/download?p=windows&channel=current&v=something" + expect(bootstrap_context.msi_url).to eq(reference_url) + end + end end context "when msi_url config option is set" do diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index 7e05bec8f7..6fcb831531 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -299,37 +299,26 @@ describe Chef::Knife do expect(Chef::Config[:log_level]).to eql(:warn) end - it "prefers the default value if no config or command line value is present and reports the source as default" do + it "prefers the default value from option definition if no config or command line value is present and reports the source as default" do knife_command = KnifeSpecs::TestYourself.new([]) # empty argv knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("default-value") + expect(knife_command.config_source(:opt_with_default)).to eq(:cli_default) end - it "prefers a value in Chef::Config[:knife] to the default" do + it "prefers a value in Chef::Config[:knife] to the default and reports the source as config" do Chef::Config[:knife][:opt_with_default] = "from-knife-config" knife_command = KnifeSpecs::TestYourself.new([]) # empty argv knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("from-knife-config") - expect(knife_command.config_source(:opt_with_default)).to eq (:config) - end - - it "correctly reports Chef::Config as the source when a a config entry comes from there" do - Chef::Config[:knife][:opt_with_default] = "from-knife-config" - knife_command = KnifeSpecs::TestYourself.new([]) # empty argv - knife_command.configure_chef - expect(knife_command.config_source(:opt_with_default)).to eq (:config) + expect(knife_command.config_source(:opt_with_default)).to eq(:config) end it "prefers a value from command line over Chef::Config and the default and reports the source as CLI" do knife_command = KnifeSpecs::TestYourself.new(["-D", "from-cli"]) knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("from-cli") - expect(knife_command.config_source(:opt_with_default)).to eq (:cli) - end - it "correctly reports CLI as the source when a config entry comes from the CLI" do - knife_command = KnifeSpecs::TestYourself.new(["-D", "from-cli"]) - knife_command.configure_chef - expect(knife_command.config_source(:opt_with_default)).to eq (:cli) + expect(knife_command.config_source(:opt_with_default)).to eq(:cli) end it "merges `listen` config to Chef::Config" do |