Skip to main content

Writing and Using Chef Handlers

Hands-On Lab

 

Photo of Keith Thompson

Keith Thompson

DevOps Training Architect II in Content

Length

01:00:00

Difficulty

Intermediate

Visibility into what is happening on our systems is incredibly important. Chef Handlers give us the ability to listen for specific events in our systems and trigger code, based on that information, such as sending notifications or logging additional information. In this hands-on lab, you'll be asked to create and deploy a custom handler that sends a request to an external web application when the chef-client run fails. By the time you've finished this lab, you should feel more comfortable creating and using handlers in your own Chef environment.

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 and Using Chef Handlers

Introduction

Visibility into what is happening on our systems is incredibly important. Chef Handlers give us the ability to listen for specific events in our systems and trigger code, based on that information, such as sending notifications or logging additional information. In this hands-on lab, you'll be asked to create and deploy a custom handler that sends a request to an external web application when the chef-client run fails. By the time you've finished this lab, you should feel more comfortable creating and using handlers in your own Chef environment.

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>

Then, change directory to chef-repo:

cd chef-repo

Create a handlers Cookbook to Contain a Custom Chef Handler

  1. Create a cookbook called handlers, using the ChefDK generator:

    chef generate cookbook cookbooks/handlers

Generate remote_error_logger Handler

  1. Change directory:

    cd cookbooks/handlers/
  2. Create the Ruby file with the handler at ~/chef-repo/cookbooks/handlers/files/default/remote_error_logger.rb, using the ChefDK file generator:

    chef generate file remote_error_logger.rb
  3. Create the recipe that will be used to deploy it:

    vim recipes/remote_error_logger.rb
  4. Add the following to the file:

    remote_logger_path = "#{Chef::Config[:file_cache_path]}/remote_error_logger.rb"
    
    cookbook_file remote_logger_path do
      source 'remote_error_logger.rb'
      action :nothing
    end.run_action(:create)
    
    chef_handler 'Handlers::RemoteErrorLogger' do
      source remote_logger_path
      type ({ exception: true, report: false })
      action :nothing
    end.run_action(:enable)

    Save and quit the recipe.

  5. Open the default recipe:

    vim recipes/default.rb
  6. Set the default recipe to automatically pull in the one we just wrote:

    include_recipe 'handlers::remote_error_logger'

    Save and quit.

  7. Open the metadata file:

    vim metadata.rb
  8. Modify the metadata, adding the following line to the bottom of the file:

    depends 'chef_handler'

    Save and quit.

  9. Run the following:

    berks install

Implement the Handler so It Sends a JSON POST Request with Information from the Run

  1. Open the file:

    vim files/default/remote_error_logger.rb
  2. Add the following to the file:

    module Handlers
      class RemoteErrorLogger < Chef::Handler
        ERROR_REPORTING_URL = 'http://error.example.com/report'
    
        def report
          if run_status.failed?
            http_client.request(post_request)
          end
        end
    
        def post_request
          request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
    
          request.body = {
            error: run_status.formatted_exception,
            node: run_status.node.name
          }.to_json
    
          request
        end
    
        def http_client
          Net::HTTP.new(uri.host, uri.port)
        end
    
        def uri
          @uri ||= URI (ERROR_REPORTING_URL)
        end
      end
    end

    Save and quit the file.

  3. Upload the file:

    berks upload

Deploy a custom handler so that it triggers when there is an error in the chef-client run.

  1. Take a look at what we already have:

    knife node show node-1 -a run_list
  2. Drop down a directory:

    cd ..
  3. List its contents:

    ls
  4. Upload the handlers cookbook to the Chef Server, add the recipe to node-1's run-list as the first recipe, and add the intentional_error recipe to the run-list, to trigger an error:

    knife node run_list set node-1 'handlers,intentional_error'
  5. Run chef-client on node-1 and verify that it fails:

    knife ssh 'name:node-1' 'sudo chef-client'
  6. Ensure journalctl --no-pager -u error_tracker | tail -1 returns a line including the JSON from the POST request.

    journalctl --no-pager -u error_tracker | tail -1

Conclusion

Congratulations on successfully completing this hands-on lab!