Error “Cannot unregister the machine” on Jenkins, ‘kitchen test’ command

2017/04/06

Ok, it’s a hack… so don’t use it unless you need it, and on your own risk.
For some reason, although the Windows vagrant box is updated using Matt Wrock’s packer templates from here https://github.com/mwrock/packer-templates, and everything goes well, and also on the first manual kitchen test on the Jenkins server everything goes ok, when run from Jenkin’s pipeline mechanism, the box is not destroyed.
Three VirtualBox processes remain hanged in the system.
It’s not a clean solution, but having this error over and over again:

STDERR: There was an error while executing `VBoxManage`, a CLI used by Vagrant for controlling VirtualBox. The command and stderr is shown below.
Command: [“unregistervm”, “some GUID here”, “–delete”]
Stderr: VBoxManage.exe: error: Cannot unregister the machine ‘kitchen-your_cookbook-default-windows2012r2_default_some_numbers’ while it is locked
VBoxManage.exe: error: Details: code VBOX_E_INVALID_OBJECT_STATE (0x80bb0007), component MachineWrap, interface IMachine, callee IUnknown

it’s really annoying, so until the final solution will reveal itself, this might also be a way:

$cookbook = ‘your_cookbook_name’
gwmi -Query “select * from win32_process where name = ‘virtualbox.exe'” | `
? { $_.CommandLine.Contains(“kitchen-$cookbook”)} | `
Select CommandLine, Handle | `
% { Get-Process -Id $_.Handle } | `
Stop-Process -Force -ErrorAction Continue

You’ll need to execute it from Groovy with the bat command, something like

bat “powershell -File \”../killVBoxes.ps1\” ${cookbookName}”

For the path of the PowerShell script you’ll need to provide the correct path.

Note: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.

Advertisements

Clean VirtualBox virtual machines used by Chef on converge

2017/03/31

From time to time I keep getting strange behavior, on local PC, with virtual machines used by Chef, maybe after a ChefDK or VirtualBox update.
Some VMs do not get destroyed properly, or maybe more than one VM is created for the same cookbook.
The following PowerShell script can be used to destroy all VMs for a Chef cookbook; it must be used from the parent folder of the cookbook, using the syntax:
.\clearCache.ps1 my_chef_cookbook .
And here the script clearCache.ps1, located one level above the cookbook, in order to be usable with all its siblings:

[CmdletBinding()]
param(
[string]$cookbook
)

# VBOX_HOME is a user environment variable specifying
# where the VirtualBox VMs are stored
# Something like C:\Users\[your_user]\VBox_VMs

$dir = (pwd).Path
cd $cookbook
Write-Output “Removing folder content for .kitchen”
rm “.\.kitchen\” -Force -Recurse

Write-Output “Power-off VMs”
((& ‘C:\Program Files\Oracle\VirtualBox\vboxmanage’ list vms) | `
  % { ($_.Replace(‘”‘,”) -split ” “)[0] } | ` # Replace double quotes with nothing
  ? { $_.Contains(“kitchen-“) }) | `
  % { & ‘C:\Program Files\Oracle\VirtualBox\vboxmanage’ controlvm $_ poweroff }
Write-Output “Delete VMs”
((& ‘C:\Program Files\Oracle\VirtualBox\vboxmanage’ list vms) | `
  % { ($_.Replace(‘”‘,”) -split ” “)[0] } | `
  ? { $_.Contains(“kitchen-“) }) | `
  % { & ‘C:\Program Files\Oracle\VirtualBox\vboxmanage’ unregistervm $_ –delete }

Write-Output “Delete the VMs again, just in case we previously had an error …”
rm “$($env.VBOX_HOME)\kitchen-$cookbook*” -Force -Recurse

Write-Output “Destroy kitchen”
kitchen destroy
rm “.\.kitchen\” -Force -Recurse
vagrant global-status –prune
Write-Output “Done”
ls
cd ..

Note: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.


Stub Chef guard_interpreter for ChefSpec test

2016/12/20

If you have a Chef cookbook with a resource containing a “guardian”, like, let’s say a PowerShell guard interpreter, you must stub the content of the Chef guard.
Here is an example of the resource using the guardian:

file “some_log_path” do
  guard_interpreter :powershell_script
  action :delete
  not_if “(ls -Dir ‘C:\temp’).Name.Contains(‘build’)”
end

Here’s how you have to construct your ChefSpec file:

describe ‘my_cookbook::my_recipe’ do
  let(:chef_run) do
    ChefSpec::ServerRunner.new do |node, server|
      stub_chef_server(node, server)
    end.converge(described_recipe)
  end

  before do
    stub_command(“(ls -Dir ‘C:\temp’).Name.Contains(‘build’)”).and_return(false)
  end

  it ‘does not raise an exception’ do
    expect { chef_run }.to_not raise_error
  end

  it ‘deletes the existing log file’ do
    expect(chef_run).to delete_file(‘C:\temp\releases\some_build\log.txt’)
  end
end


LWRP for deleting old builds

2016/12/07

In your cookbook, in libraries folder, create the following:

provider_cache_cleaner.rb

class BuildCacheCleaner
  # Provider for build_cache_cleaner
  class BuildCacheCleanerProvider < Chef::Provider::LWRPBase
    provides :build_cache_cleaner
    use_inline_resources
    action :delete do
      delete_count = 0
      if ::File.directory?(new_resource.cache_path)
        search_path = ::File.join(new_resource.cache_path.to_path, “#{new_resource.suffix}*\.#{new_resource.extension}”)
        cached_files = ::Dir.glob(search_path).sort_by { |f| ::File.ctime(f) }
        if cached_files.count > new_resource.keep_count
          delete_count = cached_files.count – new_resource.keep_count
        end
        files_to_delete = cached_files.first(delete_count)
        unless files_to_delete.empty?
          Chef::Log.info(“#{delete_count} cached builds will be deleted !”)
          files_to_delete.each do |file_name|
            Chef::Log.info(“Deleting file #{::File.basename(file_name)} …”)
            ::File.delete(file_name)
          end
        end
      end
      new_resource.updated_by_last_action(true) if delete_count > 0
    end
  end
end

resource_cache_cleaner.rb

class BuildCacheCleaner
  # Resource for build_cache_cleaner
  class BuildCacheCleanerResource < Chef::Resource::LWRPBase
    resource_name :build_cache_cleaner
    provides :build_cache_cleaner

    actions(:delete)
    default_action(:delete)

    attribute :keep_count, :kind_of => Integer, :required => false, :default => 5
    attribute :suffix, :kind_of => String, :required => false, :default => ‘Build’
    attribute :extension, :kind_of => String, :required => false, :default => ‘zip’
    attribute :cache_path, :kind_of => String, :required => false, :default => Chef::Config[:file_cache_path]
  end
end

string_util.rb

# String utility extension methods
class String
  def to_path(end_slash = false)
    “#{‘/’ if self[0] == ‘\\’}#{split(‘\\’).join(‘/’)}#{‘/’ if end_slash}”
  end
end

matchers.rb

# ChefSpec is a tool to unit test cookbooks in conjunction with rspec
# Learn more on the README or at https://github.com/sethvargo/chefspec.
if defined?(ChefSpec)
  def delete_build_cache_cleaner(resource_name)
    ChefSpec::Matchers::ResourceMatcher.new(:build_cache_cleaner, :delete, resource_name)
  end
end

In your recipe, use the resource as follow:

build_cache_cleaner ‘delete cached builds’ do
  keep_count 7
end

In your ChefSpec test, add the following:

it ‘deletes unused cached builds’ do
  expect(chef_run).to delete_build_cache_cleaner(‘delete cached builds’)
end

Note 1: Credits go to StackOverflow, IT blogs and alike, sites that helped me with the code when searching for solutions.
Note 2: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.
Note 3: While I may have tested myself most of the bits of code posted here, please be aware that you’re trying the code/scripts at your own risk, and I take no responsibility for any damage that may occur on your system(s).


Add Chef cookbook dependency in Berksfile group for testing

2016/11/24
source `https://supermarket.chef.io`

metadata

group :integration do
cookbook ‘some_cookbook’, path: ‘./test/fixtures/cookbooks/some_cookbook’

# not a strict dependency, but necessary for testing
cookbook ‘java’
end

Note 1: Credits go to StackOverflow, IT blogs and alike, sites that helped me with the code when searching for solutions.
Note 2: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.


Install latest ChefDK nightly build through crontab

2016/10/24

To install the latest ChefDK nightly build at 1 AM every day, put this in the root crontab:

0 1 * * * curl https://omnitruck.chef.io/install.sh | bash -s —- -p -P chefdk

Note 1: Credits go to StackOverflow, IT blogs and alike, sites that helped me with the code when searching for solutions.
Note 2: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.


Upload a ZIP to Azure storage account in PowerShell

2016/06/23

This was tested under PowerShell 5, with Azure PowerShell module installed.

After you download your Azure publish settings file from https://manage.windowsazure.com/publishsettings/index?client=powershell&schemaVersion= you can use Get-AzureSubscription to choose a subscription for the upload (if you have several on your account)

if(-not (Get-Module Azure)) {
  Import-Module Azure
}

Import-AzurePublishSettingsFile “path/to/your/azure/publish/settings/file”

(Get-AzureSubscription).SubscriptionName # Choose subscription if several present

Select-AzureSubscription -SubscriptionName “YourSubscritionNameHere”
$storAcc = ‘companyartifacts’
$storCntr = ‘builds’
$blob = ‘Buildxyz.zip’
$key = (Get-AzureStorageKey -StorageAccountName $storAcc).Primary

$ctxt = New-AzureStorageContext -StorageAccountName $storAcc -StorageAccountKey $key

Write-Output “Uploading ZIP to Azure…”

Set-AzureStorageBlobContent -File “path\to\Buildxyz.zip” -Container $storCntr -Blob $blob -BlobType Block -Context $ctxt -Force

Note 1: Credits go to StackOverflow, IT blogs and alike, sites that helped me with the code when searching for solutions.
Note 2: Be aware that when copy-pasting, the quotes and possibly other characters get messed up by WordPress, you’ll have to replace them.


%d bloggers like this: