├── .gitattributes
├── .rubocop.yml
├── .github
├── CODEOWNERS
└── workflows
│ ├── delivery.yml
│ └── branchcleanup.yml
├── test
├── cookbooks
│ └── test
│ │ ├── attributes
│ │ └── default.rb
│ │ ├── recipes
│ │ ├── constrained.rb
│ │ ├── rubygems_url.rb
│ │ ├── noop.rb
│ │ ├── kill.rb
│ │ ├── default.rb
│ │ ├── license.rb
│ │ ├── no_service_restart.rb
│ │ ├── direct_url.rb
│ │ └── gem_server.rb
│ │ ├── metadata.rb
│ │ └── templates
│ │ └── default
│ │ └── ufw_chef.erb
└── integration
│ ├── license
│ └── license_spec.rb
│ ├── default
│ └── default_spec.rb
│ └── noop
│ └── noop_spec.rb
├── CODE_OF_CONDUCT.md
├── TESTING.md
├── CONTRIBUTING.md
├── .delivery
└── project.toml
├── Berksfile
├── .vscode
└── extensions.json
├── spec
├── unit
│ ├── recipes
│ │ └── default_spec.rb
│ └── libraries
│ │ └── chef_client_updater_helper_spec.rb
└── spec_helper.rb
├── libraries
├── matchers.rb
└── chef_client_updater_helper.rb
├── .editorconfig
├── Gemfile
├── .gitignore
├── metadata.rb
├── kitchen.dokken.yml
├── recipes
└── default.rb
├── chefignore
├── .travis.yml
├── resources
└── default.rb
├── attributes
└── default.rb
├── kitchen.yml
├── LICENSE
├── CHANGELOG.md
├── README.md
└── providers
└── default.rb
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | TargetChefVersion: 11
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @chef-cookbooks/windows-team
2 |
--------------------------------------------------------------------------------
/test/cookbooks/test/attributes/default.rb:
--------------------------------------------------------------------------------
1 | default['chef_client_updater']['version'] = '13.10.0'
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Please refer to the Chef Community Code of Conduct at
2 |
--------------------------------------------------------------------------------
/TESTING.md:
--------------------------------------------------------------------------------
1 | Please refer to
2 |
3 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Please refer to
2 |
--------------------------------------------------------------------------------
/.delivery/project.toml:
--------------------------------------------------------------------------------
1 | remote_file = "https://raw.githubusercontent.com/chef-cookbooks/community_cookbook_tools/main/delivery/project.toml"
2 |
--------------------------------------------------------------------------------
/Berksfile:
--------------------------------------------------------------------------------
1 | source 'https://supermarket.chef.io'
2 |
3 | metadata
4 |
5 | group :integration do
6 | cookbook 'test', path: 'test/cookbooks/test'
7 | end
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "chef-software.chef",
4 | "rebornix.ruby",
5 | "editorconfig.editorconfig"
6 | ]
7 | }
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/constrained.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater 'Install constrained version' do
2 | version '12.22.5'
3 | post_install_action 'exec'
4 | end
5 |
--------------------------------------------------------------------------------
/test/integration/license/license_spec.rb:
--------------------------------------------------------------------------------
1 | describe command('chef-client -v') do
2 | target_version = '15'
3 | its('stdout') { should match "^Chef Infra Client: #{target_version}" }
4 | end
5 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/rubygems_url.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater 'Install latest Chef' do
2 | version '13.6.0'
3 | rubygems_url 'http://localhost:8808'
4 | post_install_action 'exec'
5 | end
6 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/noop.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater "Install Chef #{node['chef_client_updater']['version']}" do
2 | channel 'stable'
3 | version '12.13.37'
4 | post_install_action 'kill'
5 | end
6 |
--------------------------------------------------------------------------------
/test/cookbooks/test/metadata.rb:
--------------------------------------------------------------------------------
1 | name 'test'
2 | maintainer 'Chef Software, Inc.'
3 | maintainer_email 'cookbooks@chef.io'
4 | license 'Apache-2.0'
5 | version '1.0.0'
6 |
7 | depends 'chef_client_updater'
8 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/kill.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater "Install Chef #{node['chef_client_updater']['version']}" do
2 | channel 'stable'
3 | version node['chef_client_updater']['version']
4 | post_install_action 'kill'
5 | end
6 |
--------------------------------------------------------------------------------
/test/cookbooks/test/templates/default/ufw_chef.erb:
--------------------------------------------------------------------------------
1 | # position 50
2 | ufw allow in proto tcp to any port 22 from any
3 | <% @rubygems_ips.each do |rubygems_ip| %>
4 | <%= "ufw deny in proto tcp to #{rubygems_ip} from any" %>
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/spec/unit/recipes/default_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe 'test::default' do
4 | platform 'ubuntu', '16.04'
5 |
6 | it 'converges successfully' do
7 | expect { :chef_run }.to_not raise_error
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/default.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater "Install Chef #{node['chef_client_updater']['version']}" do
2 | channel 'stable'
3 | version node['chef_client_updater']['version']
4 | post_install_action 'exec'
5 | install_timeout 599
6 | end
7 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/license.rb:
--------------------------------------------------------------------------------
1 | node.override['chef_client']['chef_license'] = 'accept'
2 |
3 | chef_client_updater 'Install Chef Infra Client 15' do
4 | channel 'stable'
5 | version '15'
6 | post_install_action 'exec'
7 | install_timeout 599
8 | end
9 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/no_service_restart.rb:
--------------------------------------------------------------------------------
1 | chef_client_updater "Install Chef #{node['chef_client_updater']['version']}" do
2 | channel 'stable'
3 | version node['chef_client_updater']['version']
4 | event_log_service_restart false
5 | post_install_action 'kill'
6 | end
7 |
--------------------------------------------------------------------------------
/test/integration/default/default_spec.rb:
--------------------------------------------------------------------------------
1 | describe command('chef-client -v') do
2 | target_version = '13.10.0'
3 | its('stdout') { should match "^Chef: #{target_version}" }
4 | end
5 |
6 | describe command('/opt/chef/embedded/bin/gem -v') do
7 | its('stdout') { should cmp >= '2.6.11' }
8 | end
9 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'chefspec'
2 | require 'chefspec/berkshelf'
3 |
4 | RSpec.configure do |config|
5 | config.color = true # Use color in STDOUT
6 | config.formatter = :documentation # Use the specified formatter
7 | config.log_level = :error # Avoid deprecation notice SPAM
8 | end
9 |
--------------------------------------------------------------------------------
/libraries/matchers.rb:
--------------------------------------------------------------------------------
1 | # cookstyle: disable ChefModernize/DefinesChefSpecMatchers
2 | if defined?(ChefSpec)
3 | ChefSpec.define_matcher(:chef_client_updater)
4 |
5 | def update_chef_client_updater(resource_name)
6 | ChefSpec::Matchers::ResourceMatcher.new(:chef_client_updater, :update, resource_name)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/.github/workflows/delivery.yml:
--------------------------------------------------------------------------------
1 | name: delivery
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | delivery:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Check out code
12 | uses: actions/checkout@main
13 | - name: Run Chef Delivery
14 | uses: actionshub/chef-delivery@main
15 | env:
16 | CHEF_LICENSE: accept-no-persist
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root=true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | # 2 space indentation
12 | indent_style = space
13 | indent_size = 2
14 |
15 | # Avoid issues parsing cookbook files later
16 | charset = utf-8
17 |
18 | # Avoid cookstyle warnings
19 | trim_trailing_whitespace = true
20 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # TAS50: cookbook will break with the standard Gemfile, will break
2 | # testing with the latest version of kitchen-dokken in chef-dk
3 | source 'https://rubygems.org'
4 |
5 | gem 'community_cookbook_releaser'
6 | gem 'stove'
7 | gem 'test-kitchen'
8 | # testing in this cookbook will current fail with kitchen-dokken 2.x
9 | gem 'berkshelf'
10 | gem 'json'
11 | gem 'kitchen-dokken', '< 2.0'
12 | gem 'kitchen-inspec'
13 | gem 'kitchen-vagrant'
14 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/direct_url.rb:
--------------------------------------------------------------------------------
1 | raise 'This test is only for RHEL 6' unless platform_family?('rhel') && node['platform_version'].to_i == 6
2 |
3 | chef_client_updater 'Install latest Chef' do
4 | version '12.19.36'
5 | download_url_override 'https://packages.chef.io/files/stable/chef/12.19.36/el/6/chef-12.19.36-1.el6.x86_64.rpm'
6 | checksum '89e8e6e9aebe95bb31e9514052a8926f61d82067092ca3bc976b0bd223710c81'
7 | post_install_action 'exec'
8 | end
9 |
--------------------------------------------------------------------------------
/.github/workflows/branchcleanup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Branch Cleanup
3 | # This workflow is triggered on all closed pull requests.
4 | # However the script does not do anything if a merge was not performed.
5 | "on":
6 | pull_request:
7 | types: [closed]
8 |
9 | env:
10 | NO_BRANCH_DELETED_EXIT_CODE: 0
11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: jessfraz/branch-cleanup-action@master
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.rbc
2 | .config
3 | InstalledFiles
4 | lib/bundler/man
5 | pkg
6 | test/tmp
7 | test/version_tmp
8 | tmp
9 | _Store
10 | *~
11 | *#
12 | .#*
13 | \#*#
14 | *.un~
15 | *.tmp
16 | *.bk
17 | *.bkup
18 |
19 | # editor temp files
20 | .idea
21 | .*.sw[a-z]
22 |
23 | # ruby/bundler files
24 | .ruby-version
25 | .ruby-gemset
26 | .rvmrc
27 | Gemfile.lock
28 | .bundle
29 | *.gem
30 | coverage
31 | spec/reports
32 |
33 | # YARD / rdoc artifacts
34 | .yardoc
35 | _yardoc
36 | doc/
37 | rdoc
38 |
39 | # chef infra stuff
40 | Berksfile.lock
41 | .kitchen
42 | kitchen.local.yml
43 | vendor/
44 | .coverage/
45 | .zero-knife.rb
46 | Policyfile.lock.json
47 |
48 | # vagrant stuff
49 | .vagrant/
50 | .vagrant.d/
51 |
--------------------------------------------------------------------------------
/metadata.rb:
--------------------------------------------------------------------------------
1 | name 'chef_client_updater'
2 | maintainer 'Chef Software, Inc.'
3 | maintainer_email 'cookbooks@chef.io'
4 | license 'Apache-2.0'
5 | description 'Upgrades chef-client to specified releases'
6 | version '3.12.2'
7 |
8 | chef_version '>= 11' if respond_to?(:chef_version) # cookstyle: disable ChefModernize/RespondToInMetadata
9 |
10 | %w(amazon centos debian mac_os_x opensuseleap oracle redhat scientific solaris2 suse ubuntu windows aix).each do |os|
11 | supports os
12 | end
13 |
14 | unless defined?(Ridley) # cookstyle: disable ChefModernize/RespondToInMetadata
15 | source_url 'https://github.com/chef-cookbooks/chef_client_updater' if respond_to?(:source_url) # cookstyle: disable ChefModernize/RespondToInMetadata
16 | issues_url 'https://github.com/chef-cookbooks/chef_client_updater/issues' if respond_to?(:issues_url) # cookstyle: disable ChefModernize/RespondToInMetadata
17 | end
18 |
--------------------------------------------------------------------------------
/test/integration/noop/noop_spec.rb:
--------------------------------------------------------------------------------
1 | # For the noop suite, currently-installed and target Chef versions are equal.
2 | gem_path = '/opt/chef/embedded/bin/gem'
3 | chef_version = Gem::Version.new(ENV['CHEF_VERSION'] || '12.22.5')
4 |
5 | describe command("#{gem_path} -v") do
6 | # First version of Chef with bundled Rubygems >= 2.0.0.
7 | min_chef_version = '11.18.0'
8 |
9 | min_rubygems_version = '2.0.0'
10 | target_rubygems_version = '2.6.11'
11 |
12 | if Gem::Requirement.new(">= #{min_chef_version}").satisfied_by?(chef_version)
13 | its('stdout') { should cmp >= min_rubygems_version }
14 | else
15 | # Old Chef versions should upgrade old Rubygems to the target version.
16 | its('stdout') { should match target_rubygems_version }
17 | end
18 | end
19 |
20 | describe gem('mixlib-install', gem_path) do
21 | it { should be_installed }
22 | end
23 |
24 | describe gem('mixlib-versioning', gem_path) do
25 | it { should be_installed }
26 | end
27 |
--------------------------------------------------------------------------------
/test/cookbooks/test/recipes/gem_server.rb:
--------------------------------------------------------------------------------
1 | # Build a gem server for testing
2 |
3 | rubygems_ips = shell_out('dig +short rubygems.org').stdout.split("\n")
4 | deny_rubygems = rubygems_ips.map do |rubygems_ip|
5 | "ufw deny out from any to #{rubygems_ip}"
6 | end.join("\n")
7 |
8 | bash 'local gem server' do
9 | code <<-EOH
10 | #!/bin/bash -x
11 | pkill -9 gem
12 | apt-get -q -y update
13 | apt-get -q -y install ruby2.3
14 | if [[ -z `/usr/bin/gem list 'mixlib-install'` ]]; then
15 | /usr/bin/gem install 'mixlib-install'
16 | /usr/bin/gem install 'mixlib-versioning'
17 | /usr/bin/gem install 'thor' -v '0.20.0'
18 | fi
19 | /usr/bin/gem server -d /var/lib/gems/2.3.0 2>/dev/null 1>/dev/null '
16 | chef_omnibus_url: "https://omnitruck.chef.io/install.sh"
17 | data_path: test/fixtures
18 | retry_on_exit_code:
19 | - 213
20 | max_retries: 1
21 | wait_for_retry: 1
22 | client_rb:
23 | exit_status: :enabled
24 | client_fork: false
25 |
26 | verifier:
27 | name: inspec
28 |
29 | platforms:
30 | - name: ubuntu-16.04
31 | driver:
32 | image: dokken/ubuntu-16.04
33 | pid_one_command: /bin/systemd
34 | intermediate_instructions:
35 | - RUN /usr/bin/apt-get update
36 | - RUN /usr/bin/apt-get install sudo cron -y
37 |
38 | suites:
39 | - name: default
40 | run_list:
41 | - recipe[test::default]
42 | - name: license
43 | run_list:
44 | - recipe[test::license]
45 | # Test the upgrade from 14 -> 15 where the license is accepted even if all other tests upgrade
46 | # their original version to 15
47 | provisioner:
48 | product_version: '14'
49 | - name: kill
50 | run_list:
51 | - recipe[test::kill]
52 | - name: direct_url
53 | run_list:
54 | - recipe[test::direct_url]
55 | includes: centos-6
56 | - name: constrained
57 | run_list:
58 | - recipe[test::constrained]
59 | - name: noop
60 | run_list:
61 | - recipe[test::default]
62 | attributes:
63 | chef_client_updater:
64 | version: '<%= ENV["CHEF_VERSION"] || "12.22.5" %>'
65 |
--------------------------------------------------------------------------------
/recipes/default.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Author:: Tim Smith ()
3 | # Cookbook:: chef_client_updater
4 | # Recipe:: default
5 | #
6 | # Copyright:: 2016-2020, Chef Software, Inc.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # http://www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 |
20 | chef_client_updater 'update chef-client' do
21 | channel node['chef_client_updater']['channel']
22 | version node['chef_client_updater']['version']
23 | prevent_downgrade node['chef_client_updater']['prevent_downgrade']
24 | post_install_action node['chef_client_updater']['post_install_action']
25 | download_url_override node['chef_client_updater']['download_url_override'] if node['chef_client_updater']['download_url_override']
26 | checksum node['chef_client_updater']['checksum'] if node['chef_client_updater']['checksum']
27 | upgrade_delay node['chef_client_updater']['upgrade_delay'] unless node['chef_client_updater']['upgrade_delay'].nil?
28 | product_name node['chef_client_updater']['product_name'] if node['chef_client_updater']['product_name']
29 | handle_zip_download_url node['chef_client_updater']['handle_zip_download_url'] if node['chef_client_updater']['handle_zip_download_url']
30 | event_log_service_restart node['chef_client_updater']['event_log_service_restart']
31 | rubygems_url node['chef_client_updater']['rubygems_url'] if node['chef_client_updater']['rubygems_url']
32 | end
33 |
--------------------------------------------------------------------------------
/chefignore:
--------------------------------------------------------------------------------
1 | # Put files/directories that should be ignored in this file when uploading
2 | # to a Chef Infra Server or Supermarket.
3 | # Lines that start with '# ' are comments.
4 |
5 | # OS generated files #
6 | ######################
7 | .DS_Store
8 | ehthumbs.db
9 | Icon?
10 | nohup.out
11 | Thumbs.db
12 | .envrc
13 |
14 | # EDITORS #
15 | ###########
16 | .#*
17 | .project
18 | .settings
19 | *_flymake
20 | *_flymake.*
21 | *.bak
22 | *.sw[a-z]
23 | *.tmproj
24 | *~
25 | \#*
26 | REVISION
27 | TAGS*
28 | tmtags
29 | .vscode
30 | .editorconfig
31 |
32 | ## COMPILED ##
33 | ##############
34 | *.class
35 | *.com
36 | *.dll
37 | *.exe
38 | *.o
39 | *.pyc
40 | *.so
41 | */rdoc/
42 | a.out
43 | mkmf.log
44 |
45 | # Testing #
46 | ###########
47 | .circleci/*
48 | .codeclimate.yml
49 | .delivery/*
50 | .foodcritic
51 | .kitchen*
52 | .mdlrc
53 | .overcommit.yml
54 | .rspec
55 | .rubocop.yml
56 | .travis.yml
57 | .watchr
58 | .yamllint
59 | azure-pipelines.yml
60 | Dangerfile
61 | examples/*
62 | features/*
63 | Guardfile
64 | kitchen.yml*
65 | mlc_config.json
66 | Procfile
67 | Rakefile
68 | spec/*
69 | test/*
70 |
71 | # SCM #
72 | #######
73 | .git
74 | .gitattributes
75 | .gitconfig
76 | .github/*
77 | .gitignore
78 | .gitkeep
79 | .gitmodules
80 | .svn
81 | */.bzr/*
82 | */.git
83 | */.hg/*
84 | */.svn/*
85 |
86 | # Berkshelf #
87 | #############
88 | Berksfile
89 | Berksfile.lock
90 | cookbooks/*
91 | tmp
92 |
93 | # Bundler #
94 | ###########
95 | vendor/*
96 | Gemfile
97 | Gemfile.lock
98 |
99 | # Policyfile #
100 | ##############
101 | Policyfile.rb
102 | Policyfile.lock.json
103 |
104 | # Documentation #
105 | #############
106 | CODE_OF_CONDUCT*
107 | CONTRIBUTING*
108 | documentation/*
109 | TESTING*
110 | UPGRADING*
111 |
112 | # Vagrant #
113 | ###########
114 | .vagrant
115 | Vagrantfile
116 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # TAS50: THIS COOKBOOK DELIBERATELY DOES NOT USE THE STANDARD TRAVIS.YML FILE
2 | dist: xenial
3 | cache: bundler
4 |
5 | addons:
6 | apt:
7 | sources:
8 | - chef-current-xenial
9 | packages:
10 | - chef-workstation
11 |
12 | branches:
13 | only:
14 | - master
15 |
16 | before_install:
17 | - gem install bundler
18 | - bundle --version
19 | - gem update --system
20 | - gem --version
21 |
22 | env:
23 | - CHEF_LICENSE=accept
24 |
25 | matrix:
26 | include:
27 | - rvm: 2.4.5
28 | services: docker
29 | before_script:
30 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
31 | env:
32 | - CHEF_VERSION=14.12.9
33 | - KITCHEN_YAML=kitchen.dokken.yml
34 | script:
35 | # this tests that an upgrade to 15 succeeds when the license is accepted
36 | - bundle exec kitchen test license-ubuntu-1604
37 | - rvm: 2.4.5
38 | services: docker
39 | sudo: required
40 | before_script:
41 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
42 | env:
43 | - CHEF_VERSION=13.6.0
44 | - KITCHEN_YAML=kitchen.dokken.yml
45 | script:
46 | # this tests a downgrade from latest "13" to latest "12" via partial versions
47 | - bundle exec kitchen test constrained-ubuntu-1604
48 | - rvm: 2.4.5
49 | services: docker
50 | before_script:
51 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
52 | env:
53 | - CHEF_VERSION=14.1.1
54 | - KITCHEN_YAML=kitchen.dokken.yml
55 | script:
56 | - bundle exec kitchen test default-ubuntu-1604
57 | - rvm: 2.4.5
58 | services: docker
59 | before_script:
60 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
61 | env:
62 | - CHEF_VERSION=13.8.5
63 | - KITCHEN_YAML=kitchen.dokken.yml
64 | script:
65 | - bundle exec kitchen test default-ubuntu-1604
66 | - rvm: 2.4.5
67 | services: docker
68 | before_script:
69 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
70 | env:
71 | - CHEF_VERSION=12.21.31
72 | - KITCHEN_YAML=kitchen.dokken.yml
73 | script:
74 | - bundle exec kitchen test default-ubuntu-1604
75 |
--------------------------------------------------------------------------------
/resources/default.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Cookbook:: chef_client_updater
3 | # Resource:: updater
4 | #
5 | # Copyright:: 2016-2018, Will Jordan
6 | # Copyright:: 2016-2020, Chef Software Inc.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # http://www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 | #
20 |
21 | # NOTE: this cookbook uses Chef-11 backwards compatible syntax to support
22 | # upgrades from Chef 11.x and this pattern should not be copied for any modern
23 | # cookbook. This is a poor example cookbook of how to write Chef.
24 |
25 | provides :chef_client_updater
26 |
27 | actions [:update]
28 | default_action :update
29 |
30 | attribute :channel, kind_of: [String, Symbol], default: :stable
31 | attribute :prevent_downgrade, kind_of: [TrueClass, FalseClass], default: false
32 | attribute :version, kind_of: [String, Symbol], default: :latest
33 | attribute :post_install_action, kind_of: String, default: 'kill'
34 | attribute :exec_command, kind_of: String, default: $PROGRAM_NAME.split(' ').first
35 | attribute :exec_args, kind_of: Array, default: ARGV
36 | attribute :download_url_override, kind_of: String
37 | attribute :checksum, kind_of: String
38 | # If the package takes too long to install, mixlib-shellout will time out.
39 | # This enables users to adjust the timeout.
40 | attribute :install_timeout, kind_of: Integer, default: 600
41 | # If the upgrade is scheduled in the same minute that Chef runs,
42 | # there is a risk the upgrade will not be triggered.
43 | # Lowering upgrade_delay limit is not recommended.
44 | attribute :upgrade_delay, kind_of: Integer, default: 60
45 | attribute :product_name, kind_of: String, default: 'chef'
46 | attribute :rubygems_url, kind_of: String, default: lazy { Chef::Config[:rubygems_url] }
47 | attribute :handle_zip_download_url, kind_of: String, default: 'https://download.sysinternals.com/files/Handle.zip'
48 | attribute :event_log_service_restart, kind_of: [TrueClass, FalseClass], default: true
49 | attribute :install_command_options, kind_of: Hash, default: {}
50 |
--------------------------------------------------------------------------------
/attributes/default.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Cookbook:: chef_client_updater
3 | # Attributes:: default
4 | #
5 | # Copyright:: 2016-2020, Chef Software Inc.
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the 'License');
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an 'AS IS' BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 |
19 | # stable or current channel
20 | default['chef_client_updater']['channel'] = 'stable'
21 |
22 | # prevent a newer client "updating" to an older client
23 | default['chef_client_updater']['prevent_downgrade'] = false
24 |
25 | # the version to install (ex: '12.12.13') or 'latest'
26 | default['chef_client_updater']['version'] = 'latest'
27 |
28 | # kill the client post install or exec the client post install for non-service based installs
29 | default['chef_client_updater']['post_install_action'] = Chef::Config[:no_fork] ? 'exec' : 'kill'
30 |
31 | # the download URL (for use in an air-gapped environment)
32 | default['chef_client_updater']['download_url_override'] = nil
33 |
34 | # the checksum of the package from "download_url_override"
35 | default['chef_client_updater']['checksum'] = nil
36 |
37 | # Root installation path for chef-client for when a custom path is used.
38 | # Defaults to 'C:/opscode/chef' on Windows and '/opt/chef' for everything else.
39 | default['chef_client_updater']['chef_install_path'] = nil
40 |
41 | # delay for triggering Chef Infra Client upgrade in seconds
42 | default['chef_client_updater']['upgrade_delay'] = nil
43 |
44 | # name of the product to upgrade (chef or chefdk)
45 | default['chef_client_updater']['product_name'] = nil
46 |
47 | # download URL for Sysinternals handle.zip (Windows only)
48 | default['chef_client_updater']['handle_zip_download_url'] = nil
49 | default['chef_client_updater']['handle_exe_path'] = "#{Chef::Config[:file_cache_path]}/handle.exe"
50 |
51 | # The Eventlog service will be restarted immediately prior to cleanup broken chef to release any open file locks.
52 | default['chef_client_updater']['event_log_service_restart'] = true
53 |
54 | # Set to 'accept' or 'accept-no-persist' to accept the license. Provided to client execution
55 | # in a backwards compatible way. Use the same attribute from the chef-client cookbook to
56 | # avoid duplication.
57 | default['chef_client']['chef_license'] = nil
58 |
59 | # Set this to use internal or custom rubygems server.
60 | # Use the same attribute from the chef-client cookbook to avoid duplication.
61 | # Example "http://localhost:8808/"
62 | default['chef_client_updater']['rubygems_url'] = Chef::Config[:rubygems_url]
63 |
--------------------------------------------------------------------------------
/kitchen.yml:
--------------------------------------------------------------------------------
1 | driver:
2 | name: vagrant
3 |
4 | provisioner:
5 | name: chef_solo
6 | product_name: chef
7 | product_version: '<%= ENV["CHEF_VERSION"] || "12.13.37" %>'
8 | retry_on_exit_code:
9 | - 213
10 | max_retries: 1
11 | wait_for_retry: 1
12 | solo_rb:
13 | exit_status: :enabled
14 | client_fork: false
15 | log_level: :info
16 |
17 | verifier:
18 | name: inspec
19 |
20 | platforms:
21 | - name: centos-6
22 | - name: centos-7
23 | - name: centos-8
24 | - name: debian-9
25 | - name: debian-10
26 | - name: fedora-latest
27 | - name: freebsd-12
28 | - name: ubuntu-16.04
29 | - name: ubuntu-18.04
30 | - name: ubuntu-20.04
31 | - name: windows-2012r2
32 | driver:
33 | box: tas50/windows_2012r2
34 | customize:
35 | memory: 4096
36 | provisioner:
37 | wait_for_retry: 300
38 | transport:
39 | name: winrm
40 | elevated: true
41 | - name: windows-2016
42 | driver:
43 | box: tas50/windows_2016
44 | customize:
45 | memory: 4096
46 | provisioner:
47 | wait_for_retry: 300
48 | transport:
49 | name: winrm
50 | elevated: true
51 | - name: windows-2019
52 | driver:
53 | box: tas50/windows_2019
54 | customize:
55 | memory: 4096
56 | provisioner:
57 | wait_for_retry: 300
58 | transport:
59 | name: winrm
60 | elevated: true
61 | - name: macos-10.15
62 | driver:
63 | box: tas50/macos_10.15
64 | customize:
65 | memory: 4096
66 |
67 | suites:
68 | - name: default
69 | run_list:
70 | - recipe[test::default]
71 | excludes:
72 | - windows-2012r2
73 | - windows-2016
74 | - windows-2019
75 | - name: license
76 | run_list:
77 | - recipe[test::license]
78 | # Test the upgrade from 14 -> 15 where the license is accepted even if all other tests upgrade
79 | # their original version to 15
80 | provisioner:
81 | product_version: '14'
82 | - name: kill
83 | run_list:
84 | - recipe[test::kill]
85 | verifier:
86 | inspec_tests:
87 | - path: ./test/integration/default/
88 | - name: no_service_restart
89 | run_list:
90 | - recipe[test::no_service_restart]
91 | verifier:
92 | inspec_tests:
93 | - path: ./test/integration/default/
94 | - name: direct_url
95 | run_list:
96 | - recipe[test::direct_url]
97 | includes: centos-6
98 | - name: constrained
99 | run_list:
100 | - recipe[test::constrained]
101 | includes: centos-6
102 | - name: noop
103 | run_list:
104 | - recipe[test::noop]
105 | includes: centos-6
106 | - name: rubygems-url
107 | run_list:
108 | - recipe[test::gem_server]
109 | - recipe[test::rubygems_url]
110 | includes: ubuntu-16.04
111 | provisioner:
112 | product_version: 12.13.37
113 | verifier:
114 | inspec_tests:
115 | - path: ./test/integration/default/
116 |
--------------------------------------------------------------------------------
/spec/unit/libraries/chef_client_updater_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require_relative '../../../libraries/chef_client_updater_helper'
3 |
4 | describe ChefClientUpdaterHelper do
5 | let(:provider) { Object.new.extend(ChefClientUpdaterHelper) }
6 | let(:product_name) { 'chef-client' }
7 | let(:channel) { 'stable' }
8 | let(:product_version) { 'latest' }
9 | let(:install_command_options) { { option1: 'value1', option2: 'value2' } }
10 | let(:download_url_override) { 'https://my-url' }
11 | let(:checksum) { 12345 }
12 | let(:resource) do
13 | double('Chef::Resource::ChefClientUpdater',
14 | product_name: product_name, channel: channel, version: product_version, install_command_options: install_command_options,
15 | download_url_override: download_url_override, checksum: checksum)
16 | end
17 | let(:platform) { 'aix' }
18 | let(:platform_version) { '4.5' }
19 | let(:architecture) { 'x86' }
20 | let(:detected_platform) { { platform: 'aix', platform_version: '4.5', architecture: 'x86' } }
21 | let(:mixlib) { double('Mixlib::Install', new: true, detect_platform: detected_platform) }
22 | let(:options) do
23 | {
24 | product_name: product_name,
25 | platform_version_compatibility_mode: true,
26 | platform: platform,
27 | platform_version: platform_version,
28 | architecture: architecture,
29 | channel: channel.to_sym,
30 | product_version: product_version.to_sym,
31 | install_command_options: install_command_options,
32 | }
33 | end
34 |
35 | before do
36 | stub_const('::Mixlib::Install', mixlib)
37 | allow(provider).to receive(:new_resource).and_return(resource)
38 | end
39 |
40 | describe '#mixlib_install' do
41 | before do
42 | allow(provider).to receive(:load_mixlib_install)
43 | allow(Chef::Log).to receive(:debug).and_call_original
44 | allow(Chef::Log).to receive(:debug).with('Platform detected as aix by mixlib_install')
45 | end
46 |
47 | it 'calls add_download_url_options with the expected options Hash' do
48 | expect(provider).to receive(:add_download_url_override_options).with(options)
49 | provider.mixlib_install
50 | end
51 |
52 | it 'calls Mixlib::Install.new with the correct options hash' do
53 | allow(provider).to receive(:add_download_url_override_options).with(options).and_return(options)
54 | expect(Mixlib::Install).to receive(:new).with(options)
55 | provider.mixlib_install
56 | end
57 | end
58 |
59 | describe '#add_download_url_override_options' do
60 | # download_url_override nil default is never false so this is never true unless resource attribute is set to nil explicitly
61 | context 'when download_url_override is false' do
62 | before do
63 | allow(resource).to receive(:download_url_override).and_return(false)
64 | end
65 |
66 | it 'never raises an error' do
67 | expect { provider.add_download_url_override_options(options) }.to_not raise_error
68 | end
69 |
70 | it 'does not call new_resource.checksum' do
71 | expect(resource).to_not receive(:checksum)
72 | provider.add_download_url_override_options(options)
73 | end
74 |
75 | it 'does not add download url override key value pair' do
76 | expect(provider.add_download_url_override_options(options)[:install_command_options].key?(:download_url_override)).to be false
77 | end
78 | end
79 |
80 | context 'when new_resource.download_url_override is not false' do
81 | # checksum nil default is never false so this is never true unless resource.checksum is set to nil explicitly
82 | context 'when new_resource.checksum is false' do
83 | before do
84 | allow(resource).to receive(:checksum).and_return(false)
85 | end
86 |
87 | it 'raises an error that checksum must be set if download_url_override is set' do
88 | expect { provider.add_download_url_override_options(options) }.to raise_error('Using download_url_override in the chef_client_updater resource requires also setting checksum property!')
89 | end
90 | end
91 |
92 | context 'when new_resource.checksum is not false' do
93 | it 'does not raise an error' do
94 | expect { provider.add_download_url_override_options(options) }.not_to raise_error
95 | end
96 |
97 | it 'logs a debug message to Chef log' do
98 | expect(Chef::Log).to receive(:debug).with("Passing download_url_override of #{download_url_override} and checksum of #{checksum} to mixlib_install")
99 | provider.add_download_url_override_options(options)
100 | end
101 |
102 | it 'sets the options variable hash to include the download_url_override and checksum as key value pairs' do
103 | provider.add_download_url_override_options(options)
104 | expect(options[:install_command_options][:download_url_override]).to eq(download_url_override)
105 | expect(options[:install_command_options][:checksum]).to eq(checksum)
106 | end
107 | end
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # chef_client_updater Cookbook CHANGELOG
2 |
3 | This file is used to list changes made in each version of the chef_client_updater cookbook.
4 |
5 | ## 3.12.2 (2021-11-10)
6 |
7 | - Repackage to remove macOS specific files
8 |
9 | ## 3.12.1 (2021-11-04)
10 |
11 | - Fix previously incorrect changelog entry in 3.12.0
12 |
13 | ## 3.12.0 (2021-11-04)
14 |
15 | - Bump version of mixlib-install we install to 3.12 - [@gscho](https://github.com/gscho)
16 | - Enable unified_mode if possible to prevent deprecation warnings [@detjensrobert](https://github.com/detjensrobert/)
17 |
18 | ## 3.11.1 (2020-08-25)
19 |
20 | - Fix license acceptance on non-windows - [@dheerajd-msys](https://github.com/dheerajd-msys)
21 |
22 | ## 3.11.0 (2020-08-12)
23 |
24 | - #209 Fixed Windows PowerShell task reschedule by using Scheduled Task cmdlets available on PowerShell 3+ on Windows 2012+- [@jwdean](https://github.com/jwdean)
25 | - resolved cookstyle error: providers/default.rb:314:3 convention: `Style/RedundantAssignment`
26 |
27 | ## 3.10.1 (2020-05-27)
28 |
29 | - Catch when the windows upgrades fail and make sure we leave the C:\opscode\chef dir - [@teknofire](https://github.com/teknofire)
30 | - Improve logging on Windows - [@teknofire](https://github.com/teknofire)
31 | - More chef-client / chef -> Chef Infra Client - [@tas50](https://github.com/tas50)
32 | - Improve the exit message so users realize it was a success - [@tas50](https://github.com/tas50)
33 | - Expand testing with new platforms - [@tas50](https://github.com/tas50)
34 |
35 | ## 3.10.0 (2020-05-04)
36 |
37 | - Disable if_respond to cop for legacy support - [@tas50](https://github.com/tas50)
38 | - Use source parameter for chef_gem if it exists - [@ramereth](https://github.com/ramereth)
39 | - Include cinc as an allowed product_name - [@ramereth](https://github.com/ramereth)
40 |
41 | ## 3.9.0 (2020-04-14)
42 |
43 | - Remove #to_s conversion of attributes - [@jasonwbarnett](https://github.com/jasonwbarnett)
44 | - More robust eventlog and dependent services restarts on Windows - [@rabidpitbull](https://github.com/rabidpitbull)
45 |
46 | ## 3.8.6 (2020-03-06)
47 |
48 | - Fixed restart eventlog and dependent services - [@sanga1794](https://github.com/sanga1794)
49 |
50 | ## 3.8.5 (2020-03-05)
51 |
52 | - Fix nil:NilClass error - [@dheerajd-msys](https://github.com/dheerajd-msys)
53 |
54 | ## 3.8.4 (2020-02-20)
55 |
56 | -Fix the type for the rubygems_url property
57 |
58 | ## 3.8.3 (2020-02-20)
59 |
60 | - Updated rubygems_url resource property default and removed attribute - [@BrandonTheMandon](https://github.com/BrandonTheMandon)
61 |
62 | ## 3.8.2 (2020-01-14)
63 |
64 | - Remove mandatory parameters for WaitForChefClientOrRescheduleUpgradeTask - [@bdwyertech](https://github.com/bdwyertech)
65 |
66 | ## 3.8.1 (2020-01-14)
67 |
68 | - Necessary changes in PS script to accept the chef-client license while chef client upgrade in Windows. - [@Nimesh-Msys](https://github.com/Nimesh-Msys)
69 |
70 | ## 3.8.0 (2019-12-20)
71 |
72 | - Add install_command_options to enable installing Chef as a scheduled task on Windows when updating - [@gholtiii](https://github.com/gholtiii)
73 | - Fix Chef_upgrade scheduled task to reschedule itself when it fails due to running ruby.exe - [@gholtiii](https://github.com/gholtiii)
74 |
75 | ## 3.7.3 (2019-12-09)
76 |
77 | - Minor Fixes While detecting chef DK version - [@Nimesh-Msys](https://github.com/Nimesh-Msys)
78 |
79 | ## 3.7.2 (2019-11-29)
80 |
81 | - fix windows chef install path - [@phomein](https://github.com/phomein)
82 |
83 | ## 3.7.1 (2019-11-19)
84 |
85 | - Minor fixes while upgrading ChefDK - [@Nimesh-Msys](https://github.com/Nimesh-Msys)
86 | - Replace hardcode value for chef_install_path on Windows with a variable - [@dheerajd-msys](https://github.com/dheerajd-msys)
87 |
88 | ## 3.7.0 (2019-11-18)
89 |
90 | - Added noproxy support for airgapped artifact solutions [@Romascopa](https://github.com/romascopa)
91 | - Fix for using custom rubygem server - [@dheerajd-msys](https://github.com/dheerajd-msys)
92 | - Remove opensuse from the list of platforms we support as the correct platform is opensuseleap - [@tas50](https://github.com/tas50)
93 | - Remove the long_description metadata that is unused - [@tas50](https://github.com/tas50)
94 | - Disable some Cookkstyle cops that would break Chef 12 compatibility - [@tas50](https://github.com/tas50)
95 |
96 | ## 3.6.0 (2019-10-14)
97 |
98 | - Updated provider so that EventLog is properly restarted without error during convergence
99 | - Adding license acceptance support - [@tyler-ball](https://github.com/tyler-ball)
100 | - Fix creation of cron error while licence acceptance - [@NAshwini](https://github.com/NAshwini)
101 |
102 | ## 3.5.3 (2019-06-11)
103 |
104 | - Add event_log_service_restart attribute to fix issue of non chef service restart. - [@NAshwini](https://github.com/NAshwini)
105 | - Use new ChefSpec format - [@tas50](https://github.com/tas50)
106 | - Allow path to handle.exe to be configured - [@stefanwb](https://github.com/stefanwb)
107 |
108 | ## 3.5.2 (2019-01-30)
109 |
110 | - Fix rubygems upgrade logic to prevent breaking older chef-client releases such as Chef 12.X
111 |
112 | ## 3.5.1 (2018-12-22)
113 |
114 | - use --no-document for rubygems 3.0 to prevent upgrade failures - [@lamont-granquist](https://github.com/lamont-granquist)
115 |
116 | ## 3.5.0 (2018-08-31)
117 |
118 | - Only run the cron job on *nix 5 minutes from now
119 |
120 | ## 3.4.2 (2018-08-15)
121 |
122 | - Fix to retrieve current version for angrychef nodes
123 |
124 | ## 3.4.1 (2018-08-02)
125 |
126 | - Allow for a configurable package install timeout. (#130)
127 |
128 | ## 3.4.0 (2018-07-23)
129 |
130 | - Prevent failures on Chef-DK
131 | - rubygems_url: Allow the rubygems source to be specified
132 | - Require mixlib-install 3.11+ for Amazon Linux 2.0 support
133 | - Attempt to move the chef install directory if it still exists before giving up to workaround failures on Windows 2008
134 |
135 | ## 3.3.5 (2018-06-20)
136 |
137 | - Do not attempt EventLog restart on Windows 7 or Windows Server 2008 editions.
138 |
139 | ## 3.3.4 (2018-05-30)
140 |
141 | - Improve install success rate on Windows by destroying existing Chef handles prior to upgrade.
142 |
143 | ## 3.3.3 (2018-04-26)
144 |
145 | - Better support AIX by also restarting the chef-client service via cron on AIX
146 |
147 | ## 3.3.2 (2018-04-11)
148 |
149 | - Improve how we handle updating rubygems:
150 | - Update RubyGems without shelling out
151 | - Don't upgrade to the very latest version unless we have to (due to old current rubygems)
152 |
153 | ## 3.3.1 (2018-04-11)
154 |
155 | - Stop push jobs before upgrading Chef on Windows and then start it back up. This prevents failures to update.
156 |
157 | ## 3.3.0 (2018-04-10)
158 |
159 | - Post action support on Windows
160 | - Prevent failure when the user doesn't use the chef-client cookbook
161 |
162 | ## 3.2.9 (2018-04-05)
163 |
164 | - Don't run cron on Windows
165 |
166 | ## 3.2.8 (2018-04-04)
167 |
168 | - Restart chef-client with cron under sysvinit to ensure that chef-client properly restarts
169 |
170 | ## 3.2.7 (2018-03-29)
171 |
172 | - Update log message to KILL from TERM. We updated the behavior but missed the logging
173 |
174 | ## 3.2.6 (2018-03-16)
175 |
176 | - Added additional logic to decide if a 'kill' or 'exec' should be done post upgrade. If the chef-client isn't running with the supervsior process then we will no longer try to use kill.
177 |
178 | ## 3.2.5 (2018-02-28)
179 |
180 | - Use KILL instead of TERM on Windows since TERM isn't always actually killing the process
181 | - Updated the upgrade_delay value on Windows to be 60 seconds since anything less causes issues
182 |
183 | ## 3.2.4 (2018-02-05)
184 |
185 | - Fix warning '/ST is earlier than current time' on Windows
186 |
187 | ## 3.2.3 (2018-01-25)
188 |
189 | - Make product_name attribute into a real property on the resource
190 |
191 | ## 3.2.2 (2018-01-25)
192 |
193 | - Make upgrade_delay a true resource property
194 |
195 | ## 3.2.1 (2018-01-25)
196 |
197 | - Added new attribute 'upgrade_delay' that defines delay in seconds before upgrading Chef client.
198 |
199 | ## 3.2.0 (2018-01-22)
200 |
201 | - Require mixlib-install 3.9 which includes initial support for proxies and support for Amazon Linux 2.0
202 | - Add additional debug logging for the installation
203 | - If the user provides an X.Y.Z format version don't contact Chef Inc's servers to validate the version
204 | - error out chef run if shell update fails
205 |
206 | ## 3.1.3 (2017-12-20)
207 |
208 | - Support custom paths to chef-client
209 |
210 | ## 3.1.2 (2017-11-22)
211 |
212 | - Windows: Support for downgrading the Chef client
213 |
214 | ## 3.1.1 (2017-11-14)
215 |
216 | - Windows: Bypass Powershell Execution Policy for upgrade scheduled task
217 |
218 | ## 3.1.0 (2017-11-01)
219 |
220 | - Raise if download_url_override is set but checksum isn't
221 | - Require a mixin-install that supports proxies
222 | - Improve how we perform the cleanup of the previous install directory on Windows
223 | - Remove a hardcoded path to the chef-client on Windows
224 | - Improve how we perform the Windows upgrade by using a scheduled task to avoid failures during the upgrade
225 |
226 | ## 3.0.4 (2017-08-17)
227 |
228 | - Fix gem install to actually install mixlib-install 3.3.4
229 | - Fix :latest resulting in chef installing on every run
230 |
231 | ## 3.0.3 (2017-08-10)
232 |
233 | - Add accurate logging for the rubygems upgrade to reflect that we're actually upgrading to the latest release.
234 | - Require mixlib-install 3.3.4 to prevent failures on Windows nodes due to architecture detection failing within mixlib-install
235 | - Add debug logging for the desired version logic
236 | - Improve logging of the version we're upgrading to in situations where the user provides either :latest or a partial version like '12'. Show the version we're upgrading to instead of what the user passed
237 |
238 | ## 3.0.2 (2017-08-08)
239 |
240 | - Improve logging to actually log when the upgrade occurs before we kill or exec the current chef-client run
241 |
242 | ## 3.0.1 (2017-07-14)
243 |
244 | - adding check for gem.cmd on chef-client 13 windows
245 |
246 | ## 3.0.0 (2017-07-14)
247 |
248 | ### Breaking Changes
249 |
250 | - The default post install action for the resource has been switched from exec to kill. We believe that kill is the most likely default that users would expect as this allows a chef-client daemon running under a modern init system to cleanly upgrade. We highly recommend you check the readme and understand the exec and kill functions to determine which makes the most sense for how you run chef-client and how you execute the upgrade.
251 | - The prevent downgrade attribute for the default recipe has been changes from true to false as this is the default for the resource.
252 |
253 | ### Other Changes
254 |
255 | - If chef-client is forked and the user specifies an 'exec' post install action we will now warn and then kill as exec will not worked in a forked run
256 | - Updated the windows task example in the readme to properly set the start time
257 | - Updated the minimum version of mixlib-install to download 3.3.1 which includes many fixes for windows clientsA
258 | - The resource now works around situations where mixlib-install may return an Array of versions
259 |
260 | ## 2.0.3 (2017-06-27)
261 |
262 | - Fix #31 detect centos platform correctly
263 |
264 | ## 2.0.2 (2017-06-22)
265 |
266 | - Fix air-gapped installation regression introduced by support for partial versions
267 |
268 | ## 2.0.1 (2017-06-16)
269 |
270 | - Add information on upgrading Windows nodes and upgrading from Chef 11 to the readme
271 |
272 | ## 2.0.0 (2017-06-15)
273 |
274 | - The custom resource has been converted to a LWRP so that we can support Chef Client updates from chef-client 11.6.2 to current. This also removes the need for the compat_resource cookbook.
275 | - Support for upgrading Windows clients has been added
276 | - A potential infinite loop in the upgrade process has been fixed
277 | - The existing /opt/chef directory will now be cleaned up before the reinstall so leftover files will not carry over during upgrades
278 | - Full Travis testing of the cookbook has been added
279 |
280 | ## 1.1.1 (2017-05-11)
281 |
282 | - Fix the initial load of mixlib-install failing
283 |
284 | ## 1.1.0 (2017-05-10)
285 |
286 | - Add support for download URL overrides via new properties on the resource and attributes for the default recipe. This requires mixlib-install 3.2.1, which we now ensure we install in the updater resource.
287 | - Update the default post_install action in the recipe to match the resource (exec).
288 | - Remove usage of class_eval in the resource since we use compat_resource and class_eval causes issues with some later Chef 12 releases.
289 | - Fix the solaris platform name in the metadata.rb.
290 | - Remove disabling FC016 and FC023 Foodcritic rules as these no longer alert.
291 | - Avoid infinite loops if trying to install the latest chef-client version from any channel.
292 | - Add a true test recipe and remove unused inspec tests
293 | - Add debug logging of the current vs. desired versions to help troubleshooting
294 | - Added a blurb in the readme outlining init system issues surrounding kill and the chef-client starting back up
295 |
296 | ## 1.0.2 (2017-04-07)
297 |
298 | - Fix Chef 13 compatibility by using Kernel.exec not exec
299 |
300 | ## 1.0.1 (2017-04-07)
301 |
302 | - point the URLs at the new project repo
303 | - Add ChefSpec matcher
304 |
305 | ## 1.0.0
306 |
307 | - Initial release of chef_client_updater
308 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chef_client_updater
2 |
3 |  [](https://supermarket.chef.io/cookbooks/chef_client_updater)
4 |
5 | This cookbook updates the Chef Infra Client
6 |
7 | ## Requirements
8 |
9 | ### Platforms
10 |
11 | - All platforms with a Chef Infra Client package on downloads.chef.io
12 |
13 | ### Chef
14 |
15 | - Chef 11.6.2+
16 |
17 | ## Usage
18 |
19 | This cookbook provides both a custom resource and a default recipe. The default recipe simply uses the custom resource with a set of attributes. You can add chef_client_updater::default to your run list or use the custom resource in a wrapper cookbook.
20 |
21 | ### Init System Caveats
22 |
23 | When Chef Infra Client runs as a service under a system init daemon such as Sys-V or systemd each Chef Infra Client run forks off from the main chef-client process being managed by the init system. For a Chef Infra Client upgrade to occur, the running Chef Infra Client as well as the parent process must be killed, and a new Chef Infra Client must start using the updated binaries. This cookbook handles killing the chef-client process, but your init system must properly handle starting the service back up. For systemd this can be handled via configuration, and the chef-client cookbook 8.1.1 or later handles this by default. This functionality is not available in sys-v (RHEL 6, AIX, and older platforms).
24 |
25 | For systems where the init system will not properly handle starting the service back up automatically (like Sys-V or SRC) this cookbook will attempt to restart the service via a temporary cron job when either of the following conditions are met:
26 |
27 | - node['chef_client']['init_style'] == 'init'
28 | - node['chef_client_updater']['restart_chef_via_cron'] == true
29 |
30 | ### Updating Windows Nodes
31 |
32 | On Windows, a one time scheduled task `Chef_upgrade` is created in combination with a PowerShell-based upgrade script and the downloaded [Handle](https://docs.microsoft.com/en-us/sysinternals/downloads/handle) tool. After the resource stages everything for the upgrade it will terminate the client execution with exit code `213`, then the `Chef_upgrade` scheduled task will execute after `upgrade_delay` seconds (defaults to 60). The scheduled task executes the PowerShell upgrade script, which first determines whether it can successfully stop any existing Chef Infra Client service and associated Ruby processes, since the MSI Installer will fail if there is a running Chef Infra Client service or ruby.exe process. If it cannot it will sleep for one minute and try again up to five times. If after five tries it still hasn't been successful it modifies the `Chef_upgrade` scheduled task to run 10 minutes in the future to try the process again. This eliminates the situation where the scheduled task fails and the Chef Infra Client is no longer configured to run automatically (since the service has been stopped), which in large environments is costly to recover from (requiring reboots of many servers).
33 |
34 | The PowerShell upgrade script then moves the current installation to a `chef.upgrade` staging directory and that clears the way for the newer installer to run. Any existing file handles to the old installation folder are forcibly removed and the Eventlog service will be restarted immediately prior to the new installation to release any open file locks. After installation, a log file from the upgrade can be found at `c:\opscode\chef_upgrade.log` until the next Chef Infra Client run where it will be cleaned up along with the backup folder. Upon successful installation the `Chef_upgrade` scheduled task is deleted.
35 |
36 | On Windows the only supported `post_install_action` is `kill` (the property will be forced to `kill` if given `exec` and platform is Windows) because the Chef Infra Client process must be terminated before the `Chef_upgrade` scheduled task starts in order to release all open file locks by the client, otherwise the upgrade will fail with file-in-use errors.
37 |
38 | Root installation path for chef-client in cookbooks/chef_client_updater/attributes/default.rb when a custom path is used.
39 | `default['chef_client_updater']['chef_install_path'] = 'CHEF_CLIENT_FOLDER_PATH'`
40 | Defaults to 'C:/opscode/chef' on Windows and '/opt/chef' for everything else.
41 |
42 | #### Running Chef Infra Client as a Scheduled Task
43 |
44 | If you run as a scheduled task, then this will work smoothly. The path to the newly installed Chef Infra Client will be the same and the scheduled task will launch it. Part of this resource's job on the next run is to make sure the staging directory with the older client is removed.
45 |
46 | #### Running Chef Infra Client As A Windows Service
47 |
48 | If you run Chef Infra Client as a service, things get a tiny bit more complicated because when the new installer runs, the service is removed. It's recommended you migrate to running Chef Infra Client as a scheduled task as described below.
49 |
50 | #### Migrating from Running Chef Infra Client as a Windows Service to Running as a Scheduled Task During the Upgrade
51 |
52 | If you run Chef Infra Client as a service, but want to upgrade to a version of the client with an MSI unstaller that supports running as a scheduled task (any Chef Infra Client >= 12.18) it is now possible with the `install_command_options` property (added in version 3.8.0 of the chef_client_updater cookbook). This property accepts a Hash of key/value pairs, with `{daemon: 'task'}` being the necessary pair to notify the MSI Installer to install Chef Infra Client as a scheduled task.
53 |
54 | ### Upgrading from Chef Infra Client 11
55 |
56 | Moving from Chef Infra Client 11 has a few challenges when we are dealing with public update sources. Chef Infra Client 11 ships with a very old `cacert.pem`. To work through this, we need to get a more current `cacert.pem` file and point OpenSSL to it. Unfortunately, for this to work consistently on Windows, we'll need to reboot. Chef Infra Client 11 does not have the reboot resource, so this isn't a graceful process. However, on the next Chef run after the reboot, things will be back on track and the upgrade will perform as on other platforms.
57 |
58 | Below is an example of a recipe that can set up Chef Infra Client 11 to work using public update sources.
59 |
60 | ```ruby
61 | if platform_family?('windows') && (Chef::VERSION < '12')
62 | new_cert_file = File.join(ENV['USERPROFILE'], 'cacert.pem')
63 |
64 | remote_file new_cert_file do
65 | source 'https://curl.haxx.se/ca/cacert.pem'
66 | action :create
67 | end
68 |
69 | powershell_script 'restart' do
70 | code <<-EOH
71 | restart-computer -force
72 | EOH
73 | action :nothing
74 | end
75 |
76 | env 'SSL_CERT_FILE' do
77 | value new_cert_file
78 | notifies :run, 'powershell_script[restart]', :immediately
79 | end
80 | end
81 |
82 | chef_client_updater 'Install latest Chef' do
83 | post_install_action 'kill'
84 | end
85 | ```
86 |
87 | ## Chef EULA
88 |
89 | Set the `node['chef_client']['chef_license']` attribute to `accept` or `accept-no-persist` to accept the Chef EULA
90 | when upgrading to Chef Infra Client 15 or higher.
91 |
92 | ## Resources
93 |
94 | ### chef_client_updater
95 |
96 | Installs the mixlib-install/mixlib-install gems and upgrades the Chef Infra Client.
97 |
98 | #### properties
99 |
100 | - `channel` - The chef channel you fetch the Chef Infra Client from. `stable` contains all officially released Chef Infra Client builds where as `current` contains unreleased builds. Default: `stable`
101 | - `prevent_downgrade` - Don't allow this cookbook to downgrade the Chef Infra Client version. Default: `false`
102 | - `version` - The version of the Chef Infra Client to install. Default `:latest`
103 | - `post_install_action` - After installing the Chef Infra Client what should we do. `exec` to exec the new client or `kill` to kill the client and rely on the init system to start up the new version. On Windows only `kill` is supported. Default: `kill`
104 | - `exec_command` - The chef-client command. default: $PROGRAM_NAME.split(' ').first. You can also enter a custom post-action command.
105 | - `exec_args` - An array of arguments to exec the Chef Infra Client with. Default: `ARGV`
106 | - `download_url_override` - The direct URL for the Chef Infra Client package.
107 | - `checksum` - The SHA-256 checksum of the Chef Infra Client package from the direct URL.
108 | - `install_timeout` - The install timeout for non-windows systems. The default is `600`, slow machines may need to extend this.
109 | - `upgrade_delay` - The delay in seconds before the scheduled task to upgrade Chef Infra Client runs on windows. Default: `60`. Lowering this limit is not recommended.
110 | - `product_name` - The name of the product to upgrade. This can be `chef`, `chefdk`, or `cinc`. Default: `chef`
111 | - `install_command_options` - A Hash of additional options that will be passed to the Mixlib::Install instance responsible for installing the given product_name. To install Chef Infra Client as a scheduled task on windows, one can pass `{daemon: 'task'}`. Default: `{}`
112 | - `rubygems_url` - The location to source rubygems. Default: https://www.rubygems.org
113 | - `handle_zip_download_url` - Url to the Handle zip archive used by Windows. Used to override the default in airgapped environments. Default: https://download.sysinternals.com/files/Handle.zip (Note that you can also override the `default['chef_client_updater']['handle_exe_path']` attribute if you already have that binary somewhere on your system)
114 | - `event_log_service_restart` - Whether the Event Log Service on Windows should be restarted to release any locked files. Default: `true`
115 | - `install_command_options` - Additional parameters to pass to the [mixlib-install script](https://github.com/chef/mixlib-install/tree/main#install-scripts). Default: `{}`
116 |
117 | #### examples
118 |
119 | ```ruby
120 | chef_client_updater 'Install latest'
121 | ```
122 |
123 | ```ruby
124 | chef_client_updater 'Install latest Chef Infra Client 16.x' do
125 | version '16'
126 | end
127 | ```
128 |
129 | ```ruby
130 | chef_client_updater 'Install 12.13.36 and kill' do
131 | version '12.13.36'
132 | post_install_action 'kill'
133 | end
134 | ```
135 |
136 | #### Test Kitchen Testing
137 |
138 | In order to test this cookbook it will be necessary to change the `post_install_action` to `exec` from `kill`. While `kill` is better in most actual production use cases as it terminates the chef-client run along with cleaning up the parent process, the use of `kill` under test kitchen will fail the chef-client run and fail the test-kitchen run. The use of `exec` allows test-kitchen to complete and then re-runs the recipe to validate that the cookbook does not attempt to re-update the chef-client and will succeed with the new chef-client. This, however, means that it is not possible to exactly test the config which will be running in production. The best practice advice for this cookbook will be to ignore common best practices and not worry about that. If you change your production config to use `exec` in order to run what you test in test-kitchen, then you will find sharp edge cases where your production upgrades will hang and/or fail, which testing will not replicate. In order to test you should most likely test upgrades on your full-scale integration environment (not under test-kitchen) before rolling out to production and not use test-kitchen at all. If you think that there's a rule that you must test absolutely everything you run under test-kitchen, you should probably [read this](http://labs.ig.com/code-coverage-100-percent-tragedy) or [this](https://coderanger.net/overtesting/).
139 |
140 | In order to test that your recipes work under the new chef-client codebase, you should simply test your cookbooks against the new version of chef-client that you wish to deploy in "isolation" from the upgrade process. If your recipes all work on the old client, and all work on the new client, and the upgrader works, then the sum of the parts should work as well (and again, if you really deeply care about the edge conditions where that might not work -- then test on real production-like images and not with test-kitchen).
141 |
142 | #### Use of 'exec' in production
143 |
144 | This is highly discouraged since the exec will not clean up the supervising process. You're very likely to see it upgrade successfully and then see the old chef-client process continue to run and fork off copies of the old chef-client to run again. Or for the upgrade process to hang, or for other issues to occur causing failed upgrades.
145 |
146 | You can use 'exec' in production if you are running from cron or some other process manager and firing off single-shot `--no-fork` chef-client processes without using the `--interval` option. This will have the advantage that the new chef-client kicks off immediately after the upgrade giving fast feedback on any failures under the new chef-client. The utility of this approach is most likely is not enough to justify the hassle.
147 |
148 | ## A note about purpose
149 |
150 | While this cookbook supports running on Chef Infra Client versions back to 11/12, the supported behavior of the cookbook is to upgrade those versions to 13/14 or newer. It is not intended that users would maintain old Chef-11/12 versions with this cookbook. The latest released version of Chef Infra Client 12 (12.22.1 or later) is still be supported as a target. Older versions of Chef Infra Client will have their embedded rubygems force upgraded by this cookbook to avoid having to regression test against 5+ years of rubygems bugs and establish a stable basis for the cookbook to use.
151 |
152 | ## License
153 |
154 | ```text
155 | Copyright:: 2016-2020, Chef Software, Inc
156 |
157 | Licensed under the Apache License, Version 2.0 (the "License");
158 | you may not use this file except in compliance with the License.
159 | You may obtain a copy of the License at
160 |
161 | http://www.apache.org/licenses/LICENSE-2.0
162 |
163 | Unless required by applicable law or agreed to in writing, software
164 | distributed under the License is distributed on an "AS IS" BASIS,
165 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
166 | See the License for the specific language governing permissions and
167 | limitations under the License.
168 | ```
169 |
--------------------------------------------------------------------------------
/providers/default.rb:
--------------------------------------------------------------------------------
1 | #
2 | # Cookbook:: chef_client_updater
3 | # Resource:: updater
4 | #
5 | # Copyright:: 2016-2018, Will Jordan
6 | # Copyright:: 2016-2020, Chef Software Inc.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # http://www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 | #
20 |
21 | # NOTE: this cookbook uses Chef-11 backwards compatible syntax to support
22 | # upgrades from Chef 11.x and this pattern should not be copied for any modern
23 | # cookbook. This is a poor example cookbook of how to write Chef Infra code.
24 |
25 | include ::ChefClientUpdaterHelper
26 |
27 | use_inline_resources # cookstyle: disable ChefDeprecations/UseInlineResourcesDefined
28 |
29 | provides :chef_client_updater if respond_to?(:provides) # cookstyle: disable ChefModernize/RespondToProvides
30 |
31 | def load_mixlib_install
32 | gem 'mixlib-install', '~> 3.12'
33 | require 'mixlib/install'
34 | rescue LoadError
35 | Chef::Log.info('mixlib-install gem not found. Installing now')
36 | chef_gem 'mixlib-install' do
37 | version '~> 3.12'
38 | compile_time true if respond_to?(:compile_time) # cookstyle: disable ChefModernize/RespondToCompileTime
39 | if new_resource.rubygems_url
40 | clear_sources true if respond_to?(:clear_sources)
41 | if respond_to?(:source)
42 | source new_resource.rubygems_url
43 | elsif respond_to?(:options)
44 | options "--source #{new_resource.rubygems_url}"
45 | end
46 | end
47 | end
48 |
49 | require 'mixlib/install'
50 | end
51 |
52 | def load_mixlib_versioning
53 | require 'mixlib/versioning'
54 | rescue LoadError
55 | Chef::Log.info('mixlib-versioning gem not found. Installing now')
56 | chef_gem 'mixlib-versioning' do
57 | compile_time true if respond_to?(:compile_time) # cookstyle: disable ChefModernize/RespondToCompileTime
58 | if new_resource.rubygems_url
59 | clear_sources true if respond_to?(:clear_sources)
60 | options "--source #{new_resource.rubygems_url}" if respond_to?(:options)
61 | end
62 | end
63 |
64 | require 'mixlib/versioning'
65 | end
66 |
67 | def update_rubygems
68 | compatible_rubygems_versions = '>= 2.6.11'
69 | target_version = '2.7.8' # should be bumped to latest 2.x, but ruby < 2.3.0 support is necessary
70 | nodoc_rubygems_versions = '>= 3.0'
71 |
72 | rubygems_version = Gem::Version.new(Gem::VERSION)
73 | Chef::Log.debug("Found gem version #{rubygems_version}. Desired version is #{compatible_rubygems_versions}")
74 | return if Gem::Requirement.new(compatible_rubygems_versions).satisfied_by?(rubygems_version)
75 |
76 | # only rubygems >= 1.5.2 supports pinning, and we might be coming from older versions
77 | pin_rubygems_range = '>= 1.5.2'
78 | pin = Gem::Requirement.new(pin_rubygems_range).satisfied_by?(rubygems_version)
79 |
80 | converge_by "upgrade rubygems #{rubygems_version} to #{pin ? target_version : 'latest'}" do
81 | if new_resource.rubygems_url
82 | gem_bin = "#{Gem.bindir}/gem"
83 | if !::File.exist?(gem_bin) && windows?
84 | gem_bin = "#{Gem.bindir}/gem.cmd" # on Chef Infra Client 13+ the rubygem executable is gem.cmd, not gem
85 | end
86 | raise 'cannot find omnibus install' unless ::File.exist?(gem_bin)
87 | source = "--clear-sources --source #{new_resource.rubygems_url}"
88 | if Gem::Requirement.new(nodoc_rubygems_versions).satisfied_by?(rubygems_version)
89 | shell_out!("#{gem_bin} update --system #{target_version} --no-document #{source}")
90 | else
91 | shell_out!("#{gem_bin} update --system #{target_version} --no-rdoc --no-ri #{source}")
92 | end
93 | else
94 | require 'rubygems/commands/update_command'
95 | args = if Gem::Requirement.new(nodoc_rubygems_versions).satisfied_by?(rubygems_version)
96 | ['--no-document', '--system' ]
97 | else
98 | ['--no-rdoc', '--no-ri', '--system' ]
99 | end
100 | args.push(target_version) if pin
101 | Gem::Commands::UpdateCommand.new.invoke(*args)
102 | end
103 | end
104 | end
105 |
106 | def load_prerequisites!
107 | update_rubygems
108 | load_mixlib_install
109 | load_mixlib_versioning
110 | end
111 |
112 | # why would we use this when mixlib-install has a current_version method?
113 | # well mixlib-version parses the manifest JSON, which might not be there.
114 | # ohai handles this better IMO
115 | # @return String with the version details
116 | # @return nil when product is not installed
117 | #
118 | def current_version
119 | case new_resource.product_name
120 | when 'chef', 'angrychef', 'cinc', 'angrycinc'
121 | node['chef_packages']['chef']['version']
122 | when 'chefdk'
123 | versions = Mixlib::ShellOut.new('chef -v').run_command.stdout # cookstyle: disable ChefModernize/ShellOutHelper
124 | # There is a verbiage change in newer version of Chef Infra
125 | version = versions.match(/(ChefDK Version(.)*:)\s*([\d.]+)/i) || versions.match(/(Chef Development Kit Version(.)*:)\s*([\d.]+)/i)
126 |
127 | return version[-1].to_s.strip if version
128 | end
129 | end
130 |
131 | # the version we WANT TO INSTALL. If the user specifies a version in X.Y.X format
132 | # we use that without looking it up. If :latest or a non-X.Y.Z format version we
133 | # look it up with mixlib-install to determine the latest version matching the request
134 | # @return Mixlib::Versioning::Format::PartialSemVer
135 | def desired_version
136 | if new_resource.version.to_sym == :latest # we need to find what :latest really means
137 | version = Mixlib::Versioning.parse(mixlib_install.available_versions.last)
138 | Chef::Log.debug("User specified version of :latest. Looking up using mixlib-install. Value maps to #{version}.")
139 | elsif new_resource.download_url_override # probably in an air-gapped environment.
140 | version = Mixlib::Versioning.parse(new_resource.version)
141 | Chef::Log.debug("download_url_override specified. Using specified version of #{version}")
142 | elsif new_resource.version.split('.').count == 3 # X.Y.Z version format given
143 | Chef::Log.debug("User specified version of #{new_resource.version}. No need check this against Chef servers.")
144 | version = Mixlib::Versioning.parse(new_resource.version)
145 | else # lookup their shortened version to find the X.Y.Z version
146 | version = Mixlib::Versioning.parse(Array(mixlib_install.artifact_info).first.version)
147 | Chef::Log.debug("User specified version of #{new_resource.version}. Looking up using mixlib-install as this is not X.Y.Z format. Value maps to #{version}.")
148 | end
149 | version
150 | end
151 |
152 | # why wouldn't we use the built in update_available? method in mixlib-install?
153 | # well that would use current_version from mixlib-install and it has no
154 | # concept of preventing downgrades
155 | def update_necessary?
156 | load_mixlib_versioning
157 | cur_version = current_version
158 | des_version = desired_version
159 | if cur_version.nil?
160 | Chef::Log.debug("#{new_resource.product_name} is not installed. Proceeding with installing its #{des_version} version.")
161 | true
162 | else
163 | cur_version = Mixlib::Versioning.parse(current_version)
164 | Chef::Log.debug("The current #{new_resource.product_name} version is #{cur_version} and the desired version is #{des_version}")
165 | necessary = new_resource.prevent_downgrade ? (des_version > cur_version) : (des_version != cur_version)
166 | Chef::Log.debug("A #{chef_infra_product_name} upgrade #{necessary ? 'is' : "isn't"} necessary")
167 | necessary
168 | end
169 | end
170 |
171 | def eval_post_install_action
172 | return unless new_resource.post_install_action == 'exec'
173 |
174 | if Chef::Config[:interval] || Chef::Config[:client_fork]
175 | Chef::Log.warn "post_install_action \"exec\" not supported for #{chef_infra_product_name} running forked -- changing to \"kill\"."
176 | new_resource.post_install_action = 'kill'
177 | end
178 |
179 | return unless windows?
180 |
181 | Chef::Log.warn 'forcing "exec" to "kill" on windows.'
182 | new_resource.post_install_action = 'kill'
183 | end
184 |
185 | def run_post_install_action
186 | # make sure the passed action will actually work
187 | eval_post_install_action
188 |
189 | case new_resource.post_install_action
190 | when 'exec'
191 | if Chef::Config[:local_mode]
192 | Chef::Log.info 'Shutting down local-mode server.'
193 | if Chef::Application.respond_to?(:destroy_server_connectivity)
194 | Chef::Application.destroy_server_connectivity
195 | elsif defined?(Chef::LocalMode) && Chef::LocalMode.respond_to?(:destroy_server_connectivity)
196 | Chef::LocalMode.destroy_server_connectivity
197 | end
198 | end
199 | Chef::Log.warn "Replacing #{chef_infra_client_name} process with upgraded version and re-running."
200 | env = {}
201 | unless node['chef_client']['chef_license'].nil?
202 | env['CHEF_LICENSE'] = node['chef_client']['chef_license']
203 | end
204 | Kernel.exec(env, new_resource.exec_command, *new_resource.exec_args)
205 | when 'kill'
206 | if Chef::Config[:client_fork] && Process.ppid != 1 && !windows?
207 | Chef::Log.warn "#{chef_infra_product_name} is running forked with a supervisor. Sending KILL to parent process!"
208 | Process.kill('KILL', Process.ppid)
209 | end
210 | Chef::Log.warn "New #{chef_infra_product_name} installed and client process exit is allowed and/or specified. Now forcing #{chef_infra_product_name} to exit. Disregard any failure messages."
211 | exit(213)
212 | else
213 | raise "Unexpected post_install_action behavior: #{new_resource.post_install_action}"
214 | end
215 | end
216 |
217 | def chef_infra_product_name
218 | if defined?(::ChefUtils::Dist::Infra::PRODUCT)
219 | ::ChefUtils::Dist::Infra::PRODUCT
220 | else
221 | 'Chef Infra Client'
222 | end
223 | end
224 |
225 | def chef_infra_short_name
226 | if defined?(::ChefUtils::Dist::Infra::SHORT)
227 | ::ChefUtils::Dist::Infra::SHORT
228 | else
229 | 'chef'
230 | end
231 | end
232 |
233 | def chef_infra_client_name
234 | if defined?(::ChefUtils::Dist::Infra::CLIENT)
235 | ::ChefUtils::Dist::Infra::CLIENT
236 | else
237 | 'chef-client'
238 | end
239 | end
240 |
241 | def legacy_conf_dir_name
242 | if defined?(::ChefUtils::Dist::Org::LEGACY_CONF_DIR)
243 | ::ChefUtils::Dist::Org::LEGACY_CONF_DIR
244 | else
245 | 'opscode'
246 | end
247 | end
248 |
249 | def scheduled_task_name
250 | "#{new_resource.product_name}_upgrade"
251 | end
252 |
253 | def windows_chef_org_dir
254 | ::File.join('C:', legacy_conf_dir_name)
255 | end
256 |
257 | def chef_install_dir
258 | node['chef_client_updater']['chef_install_path'] || (windows? ? ::File.join(windows_chef_org_dir, chef_infra_short_name) : ::File.join('/opt', chef_infra_short_name))
259 | end
260 |
261 | def windows_upgrade_script
262 | ::File.join(windows_chef_org_dir, "#{new_resource.product_name}_upgrade.ps1")
263 | end
264 |
265 | def chef_backup_dir
266 | "#{chef_install_dir}.upgrade"
267 | end
268 |
269 | def chef_broken_dir
270 | "#{chef_install_dir}.broken"
271 | end
272 |
273 | def chef_upgrade_log
274 | ::File.join(windows_chef_org_dir, "#{new_resource.product_name}_upgrade.log")
275 | end
276 |
277 | def cron_job_name
278 | "#{new_resource.product_name}_client_updater"
279 | end
280 |
281 | # cleanup cruft from *prior* runs
282 | def cleanup
283 | if ::File.exist?(chef_backup_dir)
284 | converge_by("remove #{chef_backup_dir} from previous #{chef_infra_product_name} run") do
285 | FileUtils.rm_rf chef_backup_dir
286 | end
287 | end
288 | if ::File.exist?(chef_upgrade_log)
289 | converge_by("remove #{chef_upgrade_log} from previous #{chef_infra_product_name} run") do
290 | FileUtils.rm_rf chef_upgrade_log
291 | end
292 | end
293 | if ::File.exist?(chef_broken_dir) && new_resource.event_log_service_restart
294 | converge_by("remove #{chef_broken_dir} from previous #{chef_infra_product_name} run") do
295 | event_log_ps_code
296 | FileUtils.rm_rf chef_broken_dir
297 | end
298 | end
299 | # When running under init this cron job is created after an update
300 | cron cron_job_name do
301 | action :delete
302 | end unless platform_family?('windows')
303 | end
304 |
305 | def windows?
306 | platform_family?('windows')
307 | end
308 |
309 | def copy_opt_chef(src, dest)
310 | FileUtils.mkdir dest
311 | FileUtils.cp_r "#{src}/.", dest
312 | rescue
313 | nil
314 | end
315 |
316 | # windows does not like having running open files nuked behind it so we have to move the old file
317 | # out of the way. on both platforms we must clean up the old install to not leave behind any old
318 | # gem files.
319 | #
320 | def move_opt_chef(src, dest)
321 | converge_by("move all files under #{src} to #{dest}") do
322 | FileUtils.rm_rf dest
323 | raise "rm_rf of #{dest} failed" if ::File.exist?(dest) # detect mountpoints that were not deleted
324 | FileUtils.mv src, dest
325 | end
326 | rescue => e
327 | # this handles mountpoints
328 | converge_by("caught #{e}, falling back to copying and removing from #{src} to #{dest}") do
329 | begin
330 | FileUtils.rm_rf dest
331 | rescue
332 | nil
333 | end # mountpoints can throw EBUSY
334 | begin
335 | FileUtils.mkdir dest
336 | rescue
337 | nil
338 | end # mountpoints can throw EBUSY
339 | FileUtils.cp_r Dir.glob("#{src}/*"), dest
340 | FileUtils.rm_rf Dir.glob("#{src}/*")
341 | end
342 | end
343 |
344 | def upgrade_start_time
345 | shifted_time = Time.now + new_resource.upgrade_delay
346 | shifted_time.strftime('%H:%M')
347 | end
348 |
349 | def prepare_windows
350 | copy_opt_chef(chef_install_dir, chef_backup_dir)
351 |
352 | remote_file "#{Chef::Config[:file_cache_path]}/handle.zip" do
353 | source new_resource.handle_zip_download_url
354 | not_if { ::File.file?(node['chef_client_updater']['handle_exe_path']) }
355 | end.run_action(:create)
356 |
357 | Kernel.spawn("c:/windows/system32/schtasks.exe /F /RU SYSTEM /create /sc once /ST \"#{upgrade_start_time}\" /tn #{scheduled_task_name} /tr \"powershell.exe -ExecutionPolicy Bypass #{windows_upgrade_script} 2>&1 > #{chef_upgrade_log}\"")
358 | FileUtils.rm_rf "#{chef_install_dir}/bin/#{chef_infra_client_name}.bat"
359 | end
360 |
361 | def uninstall_ps_code
362 | <<-EOH
363 | function guid_from_regvalue($value) {
364 | $order = 7,6,5,4,3,2,1,0,11,10,9,8,15,14,13,12,17,16,19,18,21,20,23,22,25,24,27,26,29,28,31,30
365 | $dash_pos = 8,13,18,23
366 |
367 | $guid = ""
368 | $order | % {
369 | $letter = $value.Substring($_,1)
370 | $guid = "$guid$letter"
371 | if ($dash_pos -contains $guid.length) {$guid = "$guid-"}
372 | }
373 | return $guid
374 | }
375 |
376 | function installed_remove() {
377 | $installed_product = (get-item HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\C58A706DAFDB80F438DEE2BCD4DCB65C).Property
378 | $msi_guid = guid_from_regvalue $installed_product
379 |
380 | Write-Output "Removing installed product {$msi_guid}"
381 | Start-Process msiexec.exe -Wait -ArgumentList "/x {$msi_guid} /q"
382 | }
383 |
384 | Write-Output "Attempting to uninstall product"
385 | installed_remove
386 | EOH
387 | end
388 |
389 | def wait_for_chef_client_or_reschedule_upgrade_task_function
390 | <<-EOH
391 | Function WaitForChefClientOrRescheduleUpgradeTask {
392 | <# Wait for running #{chef_infra_product_name} to finish up to n times. If it has not finished after maxcount tries, then reschedule the upgrade task inMinutes minutes in the future and exit.
393 | #>
394 | param(
395 | [Parameter(Mandatory=$false)]
396 | [Int]$maxcount = 5,
397 | [Parameter(Mandatory=$false)]
398 | [Int]$inMinutes = 10
399 | )
400 |
401 | # Try maxcount times waiting for given process (#{chef_infra_client_name}) to finish before rescheduling the upgrade task inMinutes into the future
402 | $count = 0
403 | $status = (Get-WmiObject Win32_Process -Filter "name = 'ruby.exe'" | Select-Object CommandLine | select-string '#{legacy_conf_dir_name}').count
404 | while ($status -gt 0) {
405 | $count++
406 | if ($count -gt $maxcount) {
407 | Write-Output "#{chef_infra_product_name} cannot be upgraded while in use. Rescheduling the upgrade in $inMinutes minutes..."
408 | RescheduleTask #{scheduled_task_name} $inMinutes
409 | exit 0
410 | }
411 | Write-Output "#{chef_infra_product_name} cannot be upgraded while in use - Attempt $count of $maxcount. Sleeping for 60 seconds and retrying..."
412 | Start-Sleep 60
413 | $status = (Get-WmiObject Win32_Process -Filter "name = 'ruby.exe'" | Select-Object CommandLine | select-string '#{legacy_conf_dir_name}').count
414 | }
415 | }
416 | EOH
417 | end
418 |
419 | def reschedule_task_function
420 | <<-EOH
421 | Function RescheduleTask {
422 | <# Reschedule a named scheduled task the given number of minutes in the future
423 | The named scheduled task is expected to have an existing one-time TimeTrigger (which #{scheduled_task_name} has)
424 | #>
425 | param(
426 | [Parameter(Mandatory=$true)]
427 | [String]$taskName,
428 | [Parameter(Mandatory=$true)]
429 | [Int]$minutes
430 | )
431 |
432 | $newDateTime = ((Get-Date).AddMinutes($minutes)).ToString("yyyy-MM-ddTHH:mm:ss")
433 | try
434 | {
435 | $task = Get-ScheduledTask -TaskName $taskName -TaskPath '\\'
436 |
437 | # Multiple triggers or types can exist. If the first trigger is not a daily, we'll bail out.
438 | # This could be made more resilient, but the task is ours to not foul up.
439 | if (($task.Triggers[0].ToString() -eq "MSFT_TaskDailyTrigger") -or
440 | ($task.Triggers[0].ToString() -eq "MSFT_TaskTimeTrigger")) {
441 | $newTrigger = $task.Triggers[0].Clone()
442 | $newTrigger.StartBoundary = $newDateTime
443 | $task.Triggers[0].StartBoundary = $newDateTime
444 | }
445 | else {
446 | throw "Error rescheduling $taskname task trigger. Valid trigger not found."
447 | }
448 | # Assure this task will run even if the scheduled time is missed
449 | $newTaskSettings = New-ScheduledTaskSettingsSet -StartWhenAvailable
450 | # Updates the scheduled task with new setting and new trigger.
451 | $task = Set-ScheduledTask -TaskName $taskName -TaskPath '\\' -Settings $newTaskSettings -trigger $task.triggers
452 | }
453 | catch {
454 | $_.Exception.Message
455 | }
456 | }
457 | EOH
458 | end
459 |
460 | def open_handle_functions
461 | <<-EOH
462 | Function Get-OpenHandle {
463 | param(
464 | [Parameter(ValueFromPipelineByPropertyName=$true)]
465 | $Search
466 | )
467 | $handleOutput = {node['chef_client_updater']['handle_exe_path']} -accepteula -nobanner -a -u $Search
468 | $handleOutput | foreach {
469 | if ($_ -match '^(?\\S*)\\s*pid: (?\\d*)\\s*type: (?\\S*)\\s*(?\\S*)\\s*(?\\S*):\\s*(?(\\\\\\\\)|([a-z]:).*)') {
470 | $matches | select @{n="User";e={$_.user}},@{n="Path";e={$_.file}},@{n="Handle";e={$_.handle}},@{n="Type";e={$_.type}},@{n="HandlePid";e={$_.pid}},@{n="Program";e={$_.program}}
471 | }
472 | }
473 | }
474 |
475 | Function Destroy-Handle {
476 | param(
477 | [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
478 | $Handle,
479 | [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
480 | $HandlePid
481 | )
482 |
483 | $handleOutput = {node['chef_client_updater']['handle_exe_path']} -accepteula -nobanner -c $Handle -p $HandlePid -y
484 | ' Destroyed handle {0} from pid {1}' -f $Handle, $HandlePid | echo
485 | }
486 |
487 | Function Destroy-OpenChefHandles {
488 | echo '[*] Destroying open Chef handles.'
489 | Get-OpenHandle -Search '#{legacy_conf_dir_name}' | foreach {
490 | ' [+] Destroying handle that {0} (pid: {1}) has on {2}' -f $_.Program, $_.HandlePid, $_.Path | echo
491 | Destroy-Handle -Handle $_.Handle -HandlePid $_.HandlePid
492 | }
493 | echo '[*] Completed destroying open Chef handles.'
494 | }
495 | EOH
496 | end
497 |
498 | # Restart EventLog Service & its dependent services to release lock of files.
499 | def event_log_ps_code
500 | powershell_script 'EventLog Restart' do
501 | code <<-EOH
502 | $windows_kernel_version = (Get-CimInstance -class Win32_OperatingSystem).Version
503 | if (-Not ($windows_kernel_version.Contains('6.0') -or $windows_kernel_version.Contains('6.1'))) {
504 | # Get Dependent Services for Eventlog that are running
505 | $depsvcsrunning = Get-Service -Name 'EventLog' | Select-Object -ExpandProperty DependentServices |
506 | Where-Object Status -eq 'Running' | Select-Object -ExpandProperty Name
507 | # Attempt to preemptively stop Dependent Services
508 | $depsvcsrunning | ForEach-Object {
509 | Stop-Service -Name "$_" -Force -ErrorAction SilentlyContinue
510 | }
511 | # Stop EventLog Service - First Politely, then Forcibly
512 | try {
513 | Stop-Service -Name 'EventLog' -Force -ErrorAction Stop
514 | } catch {
515 | $process='svchost.exe'
516 | $data = Get-CimInstance Win32_Process -Filter "name = '$process'" | Select-Object ProcessId, CommandLine | Where-Object {$_.CommandLine -Match "LocalServiceNetworkRestricted"}
517 | $data = $data.ProcessId
518 | Stop-Process -Id $data -Force
519 | Start-Sleep -Seconds 10
520 | }
521 | # Restart EventLog Service, if Not AutoStarted
522 | $evtlogstate = Get-Service -Name 'EventLog'
523 | if ($evtlogstate.Status -eq 'Stopped') {
524 | Start-Service -Name 'EventLog'
525 | }
526 | # Restart Dependent Services - if Stopped
527 | $depsvcsrunning | ForEach-Object {
528 | $svcstate = Get-Service -Name "$_"
529 | if ($svcstate.Status -eq 'Stopped') {
530 | Start-Service -Name "$_" -ErrorAction SilentlyContinue
531 | }
532 | }
533 | }
534 | EOH
535 | end
536 | end
537 |
538 | def execute_install_script(install_script)
539 | if windows?
540 | cur_version = current_version
541 | cur_version = Mixlib::Versioning.parse(cur_version) if cur_version
542 | uninstall_if_necessary = if !cur_version.nil? && desired_version < cur_version
543 | uninstall_ps_code
544 | else
545 | ''
546 | end
547 |
548 | post_action = if new_resource.post_install_action == 'exec'
549 | new_resource.exec_command
550 | else
551 | ''
552 | end
553 |
554 | license_provided = node['chef_client']['chef_license'] || ''
555 | enforce_license = if defined?(::ChefUtils::Dist::Org::ENFORCE_LICENSE)
556 | ::ChefUtils::Dist::Org::ENFORCE_LICENSE
557 | elsif new_resource.product_name == 'chef' && desired_version.major >= 15
558 | true
559 | else
560 | false
561 | end
562 |
563 | powershell_script "#{chef_infra_product_name} Upgrade Script" do
564 | code <<-EOH
565 | $command = {
566 | $timestamp = Get-Date
567 | Write-Output "Starting upgrade at $timestamp"
568 |
569 | Get-Service #{chef_infra_client_name} -ErrorAction SilentlyContinue | stop-service
570 | Get-Service push-jobs-client -ErrorAction SilentlyContinue | stop-service
571 |
572 | #{reschedule_task_function}
573 | #{wait_for_chef_client_or_reschedule_upgrade_task_function}
574 |
575 | WaitForChefClientOrRescheduleUpgradeTask
576 |
577 | if (!(Test-Path "#{node['chef_client_updater']['handle_exe_path']}")) {
578 | Add-Type -AssemblyName System.IO.Compression.FileSystem
579 | [System.IO.Compression.ZipFile]::ExtractToDirectory("#{Chef::Config[:file_cache_path]}/handle.zip", "#{Chef::Config[:file_cache_path]}")
580 | }
581 |
582 | #{open_handle_functions}
583 |
584 | if (Test-Path "#{node['chef_client_updater']['handle_exe_path']}") {
585 | Destroy-OpenChefHandles
586 | }
587 |
588 | Remove-Item "#{chef_install_dir}" -Recurse -Force
589 |
590 | if (test-path "#{chef_install_dir}") {
591 | Write-Output "Removing #{chef_install_dir} did not completely succeed."
592 | Write-Output "It is likely now in a bad state, not even usable as a backup."
593 | Write-Output "Attempting to move #{chef_install_dir} to #{chef_broken_dir}"
594 | Move-Item "#{chef_install_dir}" "#{chef_broken_dir}"
595 | }
596 |
597 | if (test-path "#{chef_install_dir}") {
598 | Write-Output "#{chef_install_dir} still exists, upgrade will be aborted. Exiting (3)..."
599 | exit 3
600 | }
601 |
602 | #{uninstall_if_necessary}
603 |
604 | Write-Output "Running product install script..."
605 | try {
606 | #{install_script}
607 | }
608 | catch {
609 | Write-Output "An error occurred while trying to install product"
610 | Write-Output $_
611 |
612 | # Might need more testing about different ways the installation could fail
613 | Move-Item "#{chef_backup_dir}" "#{chef_install_dir}"
614 |
615 | exit 100
616 | }
617 | Write-Output "Install script finished"
618 |
619 | Remove-Item "#{windows_upgrade_script}"
620 | c:/windows/system32/schtasks.exe /delete /f /tn '#{scheduled_task_name}'
621 |
622 | $enforceLicense = $#{enforce_license.to_s}
623 |
624 | if ($enforceLicense -eq $true) {
625 |
626 | SET CHEF_LICENSE '#{license_provided}'
627 |
628 | #{chef_install_dir}/embedded/bin/ruby.exe -e "
629 | begin
630 | require 'chef/VERSION'
631 | require 'license_acceptance/acceptor'
632 | acceptor = LicenseAcceptance::Acceptor.new(provided: '#{license_provided}')
633 | if acceptor.license_required?('chef', Chef::VERSION)
634 | license_id = acceptor.id_from_mixlib('chef')
635 | acceptor.check_and_persist(license_id, Chef::VERSION)
636 | end
637 | rescue LoadError
638 | puts 'License acceptance might not be needed !'
639 | end
640 | "
641 | }
642 |
643 | #{post_action}
644 |
645 | Get-Service push-jobs-client -ErrorAction SilentlyContinue | start-service
646 |
647 | $timestamp = Get-Date
648 | Write-Output "Finished upgrade at $timestamp"
649 | }
650 |
651 | $http_proxy = $env:http_proxy
652 | $no_proxy = $env:no_proxy
653 | $set_proxy = "`$env:http_proxy=`'$http_proxy`'"
654 | $set_no_proxy = "`$env:no_proxy=`'$no_proxy`'"
655 |
656 | Set-Content -Path "#{windows_upgrade_script}" -Value "$set_proxy", "$set_no_proxy"
657 | Add-Content "#{windows_upgrade_script}" "`n$command"
658 |
659 | EOH
660 | action :nothing
661 | end.run_action(:run)
662 | else
663 | upgrade_command = Mixlib::ShellOut.new(install_script, timeout: new_resource.install_timeout)
664 | upgrade_command.run_command
665 | if upgrade_command.exitstatus != 0
666 | raise "Error updating #{chef_infra_product_name}. exit code: #{upgrade_command.exitstatus}.\nSTDERR: #{upgrade_command.stderr}\nSTDOUT: #{upgrade_command.stdout}"
667 | end
668 | end
669 | end
670 |
671 | def license_acceptance!
672 | if node['chef_client']['chef_license'].nil?
673 | Chef::Log.debug 'No license acceptance configuration found, skipping.'
674 | return
675 | end
676 |
677 | license_acceptance = shell_out("#{chef_install_dir}/bin/chef-apply -e 'exit 0'", timeout: 60, environment: { 'CHEF_LICENSE' => "#{node['chef_client']['chef_license']}" })
678 |
679 | unless license_acceptance.error?
680 | Chef::Log.debug 'Successfully accepted license.'
681 | return
682 | end
683 |
684 | msg = ['Something went wrong while accepting the license.']
685 | unless license_acceptance.stdout.empty?
686 | msg << 'STDOUT:'
687 | msg << license_acceptance.stdout
688 | end
689 | unless license_acceptance.stderr.empty?
690 | msg << 'STDERR:'
691 | msg << license_acceptance.stderr
692 | end
693 | Chef::Log.warn msg.join("\n")
694 | end
695 |
696 | action :update do
697 | begin
698 | load_prerequisites!
699 |
700 | if update_necessary?
701 | converge_by "upgrade #{new_resource.product_name} #{current_version} to #{desired_version}" do
702 | # we have to get the script from mixlib-install..
703 | install_script = mixlib_install.install_command
704 | # ...before we blow mixlib-install away
705 | platform_family?('windows') ? prepare_windows : move_opt_chef(chef_install_dir, chef_backup_dir)
706 |
707 | execute_install_script(install_script)
708 | end
709 | converge_by 'take post install action' do
710 | run_post_install_action
711 | end
712 | else
713 | cleanup
714 | end
715 | rescue SystemExit
716 | # sysvinit won't restart after we exit, potentially use cron to do so
717 | # either trust the chef-client cookbook's init scripts or the users choice
718 | if (node['chef_client'] && node['chef_client']['init_style'] == 'init') || node['chef_client_updater']['restart_chef_via_cron']
719 | Chef::Log.warn "#{chef_infra_product_name} was upgraded, scheduling #{chef_infra_product_name} start via cron in 5 minutes"
720 | cron_time = Time.now + 300
721 | start_cmd = if platform_family?('aix')
722 | '/usr/bin/startsrc -s chef > /dev/console 2>&1'
723 | else
724 | "/etc/init.d/#{chef_infra_client_name} start"
725 | end
726 |
727 | license_acceptance!
728 |
729 | r = cron cron_job_name do
730 | hour cron_time.hour
731 | minute cron_time.min
732 | command start_cmd
733 | end
734 |
735 | r.run_action(:create)
736 | end
737 |
738 | raise
739 | rescue Exception => e # rubocop:disable Lint/RescueException
740 | if ::File.exist?(chef_backup_dir)
741 | Chef::Log.warn "#{chef_infra_product_name.upcase} UPGRADE ABORTED due to #{e}: rolling back to #{chef_backup_dir} copy"
742 | move_opt_chef(chef_backup_dir, chef_install_dir) unless platform_family?('windows')
743 | else
744 | Chef::Log.warn "NO #{chef_backup_dir} DIR TO ROLL BACK TO!"
745 | end
746 | raise
747 | end
748 | end
749 |
--------------------------------------------------------------------------------