Chef Custom Resources – Useful Methods

I was tasked to write a chef custom resource (LWRP – Lightweight Resources and Providers) while working on a project to migrate the deployment of an application from shell scripts to ruby code in chef.

As defined on chef.io, a custom resource:

  • Is a simple extension of Chef
  • Is implemented as part of a cookbook
  • Follows easy, repeatable syntax patterns
  • Effectively leverages resources that are built into Chef
  • Is reusable in the same way as resources that are built into Chef

The purpose of the custom resource was to download a zipped file from the nexus repo and extract it. However, we wanted to optimize it so that it would not extract the zipped file on every chef client run unless the version of the zipped file had changed since the last client run.

One approach was to create a file as a part of the client run that would store the version number. At the next client run that file will be read and compared to the version number passed by the recipe. If the set and passed versions differ it will run the extract command.

This approach was simple and straight forward but came with the overhead of maintaining an extra file when the same can be achieved by querying the chef node object. And then, I stumbled upon “load_current_value” and “converge_if_changed” methods of the chef custom resource DSL during my research. But I was unable to find any documentation that would explain the RIGHT way to use the resource methods.

So thought of writing down my learnings with the hope that someone will benefit from it.

What is load_current_value and converge_if_changed methods?

The load_current_value method is used to load the current value of a specified attribute from the node which can then be used at the converge stage of the chef client run.

The converge_if_changed method is used inside of the action block of the custom resource. It is used to do a comparison of the value of an attribute supplied by the recipe/resource during a chef client run to the value of the attribute  loaded by the load_current_value method.

The excerpts below show the way we implemented these methods in our resource.

The version of the zipped file is supplied as a property to the resource. We set the desired_state for the property to false as we want the recipe to decide the value for this desired state property.

property :version, kind_of: String, required: true, desired_state: false

Then we call the load_current_value method to determine the value of version currently set on the node object. We use search index on the chef server to look for our node based on its fqdn. The result is filtered on the particular attribute that we are looking for (in our case t_version).

load_current_value do
  results = search(:node, "name:#{node.fqdn}",
                  :filter_result => { 't_version' => [ 'config', 't_version']}).first
  version results['t_version']
end

Next we call the converge_if_changed method on the version property to compare its value set on the node to the value supplied to the resource by the recipe. If the values differ it will converge the resources defined in its block (in our case calling the chef built-in bash resource)

action :extract do
  converge_if_changed :version do
    bash 'extract_module' do
      cwd ::File.dirname("#{Chef::Config['file_cache_path']}/#{artifact}")
      code <<-EOH
      tar xzf #{artifact} -C #{extract_path} --strip-components=1
      EOH
    end
  end
 end

That’s it !!!

References
https://docs.chef.io/dsl_custom_resource.html

” iPhoneSimulator: Timed out waiting” – Solved

Several projects were being baffled by the fact that their iOS builds were failing to launch tests. The only error that developers’ saw in the build log was :

xcodebuild[60283:14778851] iPhoneSimulator: Timed out waiting 120 seconds for simulator to boot, current state is 1.

The Xcode builds were running on the Jenkins slaves and we had applied all the rules in the book for running tests without an active GUI session i.e running the simulator in headless mode.

Remote logging into the slaves did show us that the simulator was booted up. But the tests just didn’t want to run for those projects. The confusion and frustration were compounded by the facts that we had other projects that were running tests just fine on the same/similar infrastructure.

Now while trying to debug the issue we noticed that the simulator was indeed booted but the device that it was running was not compatible with the tests that we were trying to run on it.

We were resetting our simulator i.e killing all the running instances and bringing up one just before the build starts. This was starting the simulator but with the same device that the last simulator instance used. It did not change the device based on what was being called out by the xcodebuild.

This is different to how simulator behaves when you do have an active UI session for e.g. running it on the dev machine. This basically suggested that the simulator behaviour is different when doing a build on the slaves (we are connecting to the slaves via JNLP) and that our script needs to be more intuitive when it starts the simulator.

Based on what device the build needs, we grabbed the device ID using the following command :

xcrun instruments -s | grep <required_device>

Please be aware that there might be multiple versions of the same device based on the
different SDKs that you may have installed on your OS.

The last step is to launch the simulator with the correct device ID using the following command;

open -a "iOS Simulator" --args -CurrentDeviceUDID <ID_FROM_LAST_COMMAND>

And Voila ….. All the build lights suddenly turned green..

Embedded binary validation utility error : Solved

Last week, while creating new Jenkins slaves running Yosemite and Xcode 6.3.2,  we encountered the following error when trying to build and package our existing iOS application (with Watch Kit app extension):

"Embedded binary validation utility error: Error Domain=XCEmbeddedBinaryValidationUtilityErrorDomain Code=0 "error: Embedded binary is not signed with the same certificate as the parent app. Verify the embedded binary target's code sign settings match the parent app's."

The build was running successfully on the old slaves without any issues. As the error message suggested, we started our debugging process with checking the certificate that was being used by the code signing process. But no matter what certificate we used it would spit out the same error. We tried out all possible provisions profiles and certificates combinations but to no avail.

After much digging around, we found out that it was the trust setting of the enterprise certificate that was causing the issue.

The trust setting was set to “Always Trust” . Changed it to “System Defaults” and BOOM..... the error vanished. 

Apple requires that all the certificates that are being used for code singing should have the trust setting set to “System Defaults”. A proper error message would however would have saved me a lot of time and effort 🙂