Skip to main content

Writing Custom Resources for Chef

Hands-On Lab

 

Photo of Keith Thompson

Keith Thompson

DevOps Training Architect II in Content

Length

01:00:00

Difficulty

Intermediate

Removing duplication in code is one of the easiest ways to improve maintainability. This is true within Chef recipes. Extracting code to be reused in Chef is often done by creating custom resources. In this hands-on lab, you'll be asked to create a custom resource. It needs to easily set up a new user on a node, then install packages that the user would like from a data bag. By the time you've finished this lab, you should feel more comfortable creating reusable custom resources for Chef.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Writing Custom Resources for Chef

Introduction

Removing duplication in code is one of the easiest ways to improve maintainability. This is true within Chef recipes. Extracting code to be reused in Chef is often done by creating custom resources. In this hands-on lab, you'll be asked to create a custom resource. It needs to easily set up a new user on a node, then install packages that the user would like from a data bag. By the time you've finished this lab, you should feel more comfortable creating reusable custom resources for Chef.

Solution

Open a terminal session and log in to the Workstation server via SSH using the credentials listed on the lab page:

ssh cloud_user@<PUBLIC IP>

Create a Custom Resource File in bootstrap

  1. Change to the bootstrap directory:

    cd chef-repo/cookbooks/bootstrap
  2. Run the unit test:

    chef exec rspec

    This will display failures, since we don't have a custom resource.

  3. Run the integration test:

    kitchen test

    This will display an error for an undefined method, since it doesn't currently exist. But now we know we can run our tests, and they're failing for the right reasons.

  4. Create the file that will contain the resource in the bootstrap cookbook:

    chef generate resource system_setup

Customize the Resource Name to system_setup

  1. Open the file:

    vim resources/system_setup.rb
  2. Change the custom resource name from the default bootstrap_system_setup to system_setup:

    resource_name :system_setup

    Save and quit the file.

Get Unit and Integration Tests Passing

  1. Run the unit test:

    chef exec rspec

    This gives us manage_user errors.

  2. Open the Berksfile:

    vim Berksfile

    There, we'll see we have a testing cookbook set up.

  3. Open the resources/system_setup.rb file:

    vim resources/system_setup.rb
  4. Edit the file as follows:

    resource_name :system_setup
    
    property :manage_user, [true, false], default: true
    property :data_bag, String

    Save and quit the file.

  5. Run the unit test:

    chef exec rspec

    Now we're getting errors because we have to set it to an action that exists.

  6. Open the resources/system_setup.rb file:

    vim resources/system_setup.rb
  7. Create the actions by editing the file as follows:

    resource_name :system_setup
    
    property :manage_user, [true, false], default: true
    property :data_bag, String
    
    action :create do
    end
    
    action :delete do
    end

    Save and quit the file.

  8. Run the unit test:

    chef exec rspec

    We'll see a few things we need to fix. Let's tackle them from top to bottom.

  9. Open the resources/system_setup.rb file:

     vim resources/system_setup.rb
  10. Edit the file as follows:

    resource_name :system_setup
    
    property :manage_user, [true, false], default: true
    property :data_bag, String
    
    action :create do
      if new_resource.manage_user
        user new_resource.name do
          action :create
        end
      end
    
      user_packages = data_bag_item(
        new_resource.data_bag,
        new_resource.name
      )['packages'] || []
    
      user_packages.each do |item|
        package item
      end
    end
    
    action :delete do
      if new_resource.manage_user
        user new_resource.name do
          action :remove
        end
      end
    end

    Save and quit the file.

  11. Run the unit test:

    chef exec rspec

    We'll get an error since we haven't set a default data bag.

  12. Open the resources/system_setup.rb file:

    vim resources/system_setup.rb
  13. Edit the file as follows:

    resource_name :system_setup
    
    property :manage_user, [true, false], default: true
    property :data_bag, String, default: 'system_setups'
    
    action :create do
      if new_resource.manage_user
        user new_resource.name do
          action :create
        end
      end
    
      user_packages = data_bag_item(
        new_resource.data_bag,
        new_resource.name
      )['packages'] || []
    
      user_packages.each do |item|
        package item
      end
    end
    
    action :delete do
      if new_resource.manage_user
        user new_resource.name do
          action :remove
        end
      end
    end

    Save and quit the file.

  14. Run the unit test:

    chef exec rspec

    This time, we should have no failures.

  15. Run the integration test:

    kitchen test

    For the most part, the test passed, except that we aren't managing the directory for the david user.

  16. Open the resources/system_setup.rb file:

    vim resources/system_setup.rb
  17. Edit the file as follows:

    resource_name :system_setup
    
    property :manage_user, [true, false], default: true
    property :data_bag, String, default: 'system_setups'
    
    action :create do
      if new_resource.manage_user
        user new_resource.name do
          manage_home new_resource.manage_user
          action :create
        end
      end
    
      user_packages = data_bag_item(
        new_resource.data_bag,
        new_resource.name
      )['packages'] || []
    
      user_packages.each do |item|
        package item
      end
    end
    
    action :delete do
      if new_resource.manage_user
        user new_resource.name do
          action :remove
        end
      end
    end

    Save and quit the file.

  18. Run the integration test:

    kitchen test

    This time, it should successfully run.

Conclusion

Congratulations on successfully completing this hands-on lab!