Updating Windows Puppet Enterprise agent versions with puppet_agent

Yesterday, I tackled upgrading Linux Puppet Enterprise agents with puppet_agent and said I would give Windows agents a try next. It’s simultaneously extremely easy and really difficult to accomplish this! On the manifest side, it’s as simple as including the class in a profile for Windows agents. On the rspec side, we need to add a lot of custom facts and even mock up some functions. I also encountered an issue with version 1.3.1 that broke my rspec tests. Version 1.3.2 was released in February 2017 to address that.

As usual, let’s start with the rspec test updates. Here’s the starting tests and manifest for an empty profile::base::windows, where nothing is really managed in the base profile yet.

# spec/classes/profile/base__windows_spec.rb
require 'spec_helper'
describe 'profile::base::windows', :type => :class do
  let :facts do
    {
      :kernel => 'windows',
      :operatingsystem => 'windows',
      :operatingsystemrelease => '2012 R2',
      :puppetversion => '4.3.1',
    }
  end

  context 'with defaults for all parameters' do
    it { is_expected.to create_class('profile::base::windows') }
  end
end

# dist/profile/manifests/base/windows.pp
class profile::base::windows {

}

There’s nothing special here. The tests pass, however. Here’s a first pass at the updates, which will generate a lot of failures in testing:

# spec/classes/profile/base__windows_spec.rb
require 'spec_helper'
describe 'profile::base::windows', :type => :class do
  let :facts do
    {
      :kernel => 'windows',
      :operatingsystem => 'windows',
      :operatingsystemrelease => '2012 R2',
      :puppetversion => '4.3.1',
    }
  end

  context 'with defaults for all parameters' do
    it { is_expected.to create_class('profile::base::windows') }

    it { is_expected.to contain_class('puppet_agent') }
  end
end

# dist/profile/manifests/base/windows.pp
class profile::base::windows (
  $manage_puppet_agent = true, # Manage the puppet agent on nodes
) {
  if $manage_puppet_agent {
    include puppet_agent
  }
}

Right now, the manifest applies cleanly! If you’ve set your puppet_agent::package_version in hiera where it will apply to Windows agents, they’ll get upgraded smoothly. However, your tests are going to start turning red for a few reasons. There are custom facts from puppet_agent that we need to provide values to. For Windows, it also depends on some functions from the master, so we have to mock up the functions and return values. There are also a few other Windows-related facts that the module cares about that probably aren’t in your custom facts setup, even if you switch to using rspec-puppet-facts to get a majority of Windows facts, as I did. Here’s the rewritten test:

# spec/classes/profile/base__windows_spec.rb
require 'spec_helper'

aio_build = '1.3.6' # Corresponds to puppet 4.3.6

describe 'profile::base::windows', :type => :class do
  on_supported_os.each do |os, facts|
    next unless facts[:kernel] == 'windows'
    context "on #{os}" do

      # Required to mock up functions puppet_agent relies on
      before (:each) do
        Puppet::Parser::Functions.newfunction(:pe_build_version, type: :rvalue) do |args|
          '4.0.0'
        end

        Puppet::Parser::Functions.newfunction(:pe_compiling_server_aio_build, type: :rvalue) do |args|
          aio_build
        end
      end

      let (:appdata) { 'C:\ProgramData' }

      let (:facts) {
        facts.merge(
          clientcert: 'puppet_agent_windows',
          # required for puppet_agent
          common_appdata: appdata,
          puppet_confdir: "#{appdata}/PuppetLabs/puppet/etc",
          mco_confdir: "#{appdata}/Puppetlabs/mcollective/etc",
          is_pe: true,
          aio_agent_version: aio_build,
          env_temp_variable: 'C:\tmp',
          puppet_master_server: 'puppet.example.com',
          puppet_agent_pid: 42,
          puppet_client_datadir: 'C:/ProgramData/PuppetLabs/puppet/cache/client_data'
        )
      }

      context 'with defaults for all parameters' do
        it { is_expected.to create_class('profile::base::windows') }

        it { is_expected.to contain_class('puppet_agent') }
      end
    end
  end
end

We’re almost there. If you run rspec now, you’ll still get some errors:

$ bundle exec rspec spec/classes/profile/base__windows_spec.rb
[Coveralls] Set up the SimpleCov formatter.
[Coveralls] Using SimpleCov's default settings.

profile::base::windows
  on windows-2012 R2-x64
    with defaults for all parameters
      should contain Class[profile::base::windows] (FAILED - 1)
      should contain Class[puppet_agent] (FAILED - 2)

Failures:

  1) profile::base::windows on windows-2012 R2-x64 with defaults for all parameters should contain Class[profile::base::windows]
     Failure/Error: include puppet_agent

     Puppet::PreformattedError:
       Evaluation Error: Unknown variable: '::puppet_agent::params::_source'. at /home/rnelson0/puppet/controlrepo/spec/fixtures/modules/puppet_agent/manifests/init.pp:47:22 on node build.nelson.va
 ...

There are two contributing problems to this. The one we can workaround is that in Puppet 4, strict_variables defaults to enabled. When $puppet_agent::params::_source is not set but referenced, it generates an error. To get around that, we provide an environment variable when running the test:

$ STRICT_VARIABLES="no" bundle exec rspec spec/classes/profile/base__windows_spec.rb
[Coveralls] Set up the SimpleCov formatter.
[Coveralls] Using SimpleCov's default settings.

profile::base::windows
  on windows-2012 R2-x64
    with defaults for all parameters
      should contain Class[profile::base::windows]
      should contain Class[puppet_agent]

Finished in 2.26 seconds (files took 1.99 seconds to load)
2 examples, 0 failures

Of course, that’s just a surface error. The real issue is that $_source is not populated for the windows osfamily. There are a few other variables that are not set before usage for Windows, so you may see rspec failing on those variables before $puppet_agent::params::_source. PR203 aims to fix all of these unset variables for Windows. Until then, you’ll need to use the STRICT_VARIABLES="no" workaround or live with a few red marks in your testing.

Now that we have a working manifest and test, we can have a Windows agent check in against the environment and get an upgrade. Launch Start Command Prompt with Puppet as an administrator and check in from there:

C:\Windows\system32>puppet --version
4.3.2

C:\Windows\system32>puppet agent -t --environment puppet_agent
Info: Using configured environment 'puppet_agent'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/facter/env_temp_variable.rb]/ensure: defined content as '{md5}cdbfcfd64baae73ea09d5e5258eaafe1'
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/facter/mco_config.rb]/ensure: defined content as '{md5}935de31c8b94826f513a930434ca98d8'
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/facter/puppet_agent_pid.rb]/ensure: defined content as '{md5}77e8c25824589f5e62b279698a25e78c'
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/facter/settings.rb]/ensure: defined content as '{md5}9131d59ebd9768b7410dbb835e7a1f2a'
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/puppet/parser/functions/uri_host_from_string.rb]/ensure: defined content as '{md5}97b3d860d1b78909d38e17bfa9932874'
Notice: /File[C:/ProgramData/PuppetLabs/puppet/cache/lib/puppet/parser/functions/windows_native_path.rb]/ensure: defined content as '{md5}ffaf8b3778cd9be2a7d98ea51d14cd5e'
Info: Loading facts
Info: Caching catalog for windows.example.com
Info: Applying configuration version '1483544760'
Notice: /Stage[main]/Puppet_agent::Prepare::Puppet_config/Ini_setting[main/pluginsync]/ensure: removed
Notice: /Stage[main]/Puppet_agent::Prepare::Package/File[C:\ProgramData\Puppetlabs\packages]/ensure: created
Notice: /Stage[main]/Puppet_agent::Prepare::Package/File[C:\ProgramData\Puppetlabs\packages\puppet-agent-x64.msi]/ensure: defined content as '{sha256lite}01009e50abac71f031a3204235a9864fe3be292d602cef645fe1a47ea8672dbd'
Notice: /Stage[main]/Puppet_agent::Windows::Install/File[C:\Users\rnelson0\AppData\Local\Temp\install_puppet.bat]/ensure: defined content as '{md5}5992d0057b05bce4b3b9393b22bc6efe'
Notice: /Stage[main]/Puppet_agent::Windows::Install/Exec[install_puppet.bat]/returns: executed successfully
Notice: /Stage[main]/Puppet_agent::Windows::Install/Exec[fix inheritable SYSTEM perms]/returns: executed successfully
Notice: Applied catalog in 10.35 seconds

C:\Windows\system32>puppet --version
'puppet' is not recognized as an internal or external command,
operable program or batch file.

That seems scary, but it’s not! We just need to close the command prompt and launch Start Command Prompt with Puppet as administrator again. You’ll notice it has a new icon from the upgraded puppet agent version. The command prompt environment changed slightly and the new one sees puppet as we would expect:

C:\Windows\system32>puppet --version
4.7.0

You’re almost ready to merge these changes to production so that you can keep your Windows and Linux agents up to date! You will need to update your CI tests to disable STRICT_VARIABLES, test using a fork of the puppet_agent module (my branch is here), or maybe even delay merging until PR203 is merged. I definitely would not suggest any option that results in CI tests going red, as that will prevent you from finding regressions in the future – and may even sabotage overall CI efforts if people decide they can’t trust it! It may make sense to split the Windows and Linux commits into two branches and only merge the Linux changes right now. You’ll have to figure out what works for your organization.

This was a little more painful than I imagined, and somewhat annoying that the Windows changes need delayed or implemented with temporary workarounds, but the up front work should save a lot of work later. Hopefully this is helpful to your efforts, as well. Thanks!

2 thoughts on “Updating Windows Puppet Enterprise agent versions with puppet_agent

  1. Pingback: Upgrading Puppet OpenSource Agents with puppet_agent and jlambert121/puppet | rnelson0
  2. Pingback: Upgrading Puppet Enterprise from 2016.4 to 2017.3 | rnelson0

Leave a comment