Table of contents
1.
Introduction
1.1.
Knife Plugin
2.
Syntax
3.
Namespace
4.
Class Name
5.
Banner
6.
Dependencies and Requirements
7.
Options
8.
Arguments
9.
Configuration Settings
10.
Search
11.
User Interaction
12.
Executing a Plugin
13.
Frequently Asked Questions
13.1.
What is a recipe in Chef?
13.2.
What do you mean by a Cookbook in chef?
13.3.
How was Chef started?
14.
Conclusion
Last Updated: Mar 27, 2024
Easy

About Custom Knife Plugins in Chef

Author Lokesh Sharma
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Hey ninja! Welcome to the blog. We are going to predict something about you now. Within 15 minutes, you will master Knife plugins in Chef ^-^. You heard that right! In this blog, we will discuss plugins in Chef. We will learn to create custom plugins that suit our requirements. Initially, we will look at the definition and syntax of the custom plugin.

Custom Knife Plugins in Chef

These plugins have various components. We will discuss each of these components in detail. By the end of the blog, you will be able to create or use existing Knife plugins.

Knife Plugin

Let us first start by understanding what a Knife and knife plugin is. 

A knife is the chef’s command line tool that forms the bridge between the local system machine and the chef’s Infra server. The Infra server is the configuration engine of the Chef. Plugins are collections of sub-commands and increase the features and functionalities of Knife’s built-in sub-commands. We can create custom plugins according to our needs. 

Since the chef is an open-source platform, many custom knife plugins are available to the public. We can use our custom plugin in different projects and distribute it in the community.

Syntax

require 'chef/knife'

# other require attributes, as needed

module ModuleName

 class SubclassName < Chef::Knife
 
   deps do
     require 'chef/dependency'
     # other dependencies, as needed
   end
   
   banner "knife subcommand argument VALUE (options)"
   option :name_of_option,
     :short => "-l VALUE",
     :long => "--long-option-name VALUE",
     :description => "The description for the option.",
     :proc => Proc.new { code_to_run }
     :boolean => true | false
     :default => default_value
     
   def run
     # Ruby code goes here
   end
 end
end

 

Let us discuss each component of the syntax one by one.

Namespace

A namespace is a declaration that provides various variables and keywords with similar names. Every knife plugin that you create should have its namespace. 

We use the module keyword to declare a namespace.

Syntax: module <your namespace name>. This statement tells the compiler about the name of your namespace. 

Declaring a namespace is essential because it helps in the logical organization of code.

Declaring of namespace is followed by mentioning the class name

Class Name

We declare the class name of our plugin as a subclass of both knife and chef. In terms of object-oriented programming, this declaration is called inheritance. Our plugin will now inherit all the functionalities of knife and Chef.

Syntax: class <your subclass name> < Knife:: Chef.

This statement creates a children class from Knife and Chef class. Note that it is standard to use CamelCasing to declare subclass names. This style is traditional to all subclass. 

Note: If you want to override an existing knife or chef subcommand, you can use the same name as the subcommand’s class name while declaring.

If you are willing to share your custom plugin with the community, you must create it in a user-friendly way. The Banner section helps you in this case. 

Banner

Suppose a user uses your plugin for the first time. He will indeed require some documentation to use it. The banner method is used to provide that documentation. Whatever you put inside the banner will appear on the screen when the user types the -- help command in the command line.

Example:

module Delete
 class delete < Chef::Knife
 
 banner "this commands allows you to delete an object"
 .
 .
 .
end

 

Now, you may create a plugin that offers the same functionalities as some other plugins. Writing the code for enabling these features is meaningless. It will make your code repetitive and extensive. Instead, you can declare these plugins as dependencies and access their features.

Dependencies and Requirements

 We use the deps method and the require keyword to load these plugins in the knife. The good part about using deps is that it loads only those plugins that our plugin requires. This strategy reduces the overhead and saves time. 

Syntax: 

class subclassName < Chef::Knife

deps do
 require <plugin path>
end

 

Generally, all the path plugins are in the knife directory inside the chef folder.

We suggest you type the location of your required plugin very carefully. 

Here the deps keyword declares the starting of dependencies. The require keyword finds the plugins at the given location and loads them.

Options

Using the options keyword, we can also add various command line options, such as show, status, tag, etc. An option can have three types of values. 

  1. Boolean that is true or false.
  2. String value.
  3. Piece of executable code.

After typing the plugin keyword, you can use these options by writing in the - <options> format. We can view the list of options for a particular command by typing  - - help after the command.

For example

option :URL,
 :short => "-s URL_",
 :long => "--url-infra-server",
 :description => "The URL of the Chef’s infra server."

 

We may also use options to update a particular class's run method and execute different conditions based on the option's value. 

 

Syntax:

def run
 if config[:<option>]
   # condition statement 1
<code> 

 else
   # condition statement 2
<code>
 end
end

Arguments

Command line arguments are the inputs to commands.

For example,  knife list roles/ has knife list as the command and roles/ as the argument. 

We use the name_args method to add command line arguments to our plugin.

We suggest you check the size of the argument list before executing. If the number of arguments present is insufficient, you must exit the execution. 

For example,

def run
#ensures that there is at least one argument present.
 unless name_args.size == 1
   <code>
   
#displays the right usage before exiting, in case of command failure.
   show_usage 
   exit 1
 end
 
 first_arg = name_args.first
 if config[:option]
   <condtional code>
 else
   <conditional code>
 end
end

 

Here, first_arg gets the first argument from the list of ideas. 

Configuration Settings

A configuration file specifies the configuration settings for a knife. In Ruby, this file is named config.rb. This file loads every time we execute a knife. We can set the knife plugin settings in the configuration file in the following two ways:

  • We can use the “: proc” attribute of the option method.  
     
Syntax: :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key 
Chef::Config[:knife][:<setting_name>]
  • We can specify the configurations directly inside the def method. We can use either Chef::Config[:knife][:setting_name] or use config[:setting_name].

This method is quite useful when we use a particular setting frequently. You can easily configure that setting according to your requirements. 

Now, we may often want to receive data from the Chef’s infra server. This data may have cookbook records or information about nodes. So, to search and receive data from the servers, we need to implement the search functionality in our plugin.

Search

We first import the search functionality using the require method. 

require 'chef/search/query’

We then create a search query. This query is created as a ruby object using the Query subclass. Chef::Search::Query.new

 

We then assign a variable name to it for further use. 

<variable_name> = Chef::Search::Query.new .

You can now use this search object to find your relevant data in the Chef servers. 

For example

query = "role:webserver"
# searches the object query_nodes and stores the result in the node_item variable.

query_nodes.search('node', query) do |node_item|
# returns the name of the nodes found.
puts "Node Name: #{node_item.name}"
end

The node_item variable will store the query result. We can use this result later for other operations.

It is always good to have a proper user interface for your application. A beautiful, interactive layout is always easy to use and understand. Therefore, we advise you to create a knife plugin with a good UI. 

User Interaction

The “ui” object provides several methods for user interactions in our plugin. We will look at a few of these methods below:

  1. ui.color  => It takes two arguments, the string and the color. The color is applied to that string. It provides font styling to commands such as bold, italic, etc. Example:  ui.color('Class', :italics).
     
  2. ui.edit_data => This interface property edits the data provided as input. It takes data as an argument and an optional argument, parse_output. Syntax: ui.edit_data(data, parse_output=true).
     
  3. ui.error => Presents text as an error message to the user.
     
  4. ui.fatal  => Presents text as a fatal error to the user.
     
  5. ui.warn => gives a warning to the user.
     
  6. ui.msg(message) => presents the user with a message.
     

An example to show user interaction in a plugin:

unless name_args.size == 1
 ui.warn("This is a warning statement.")
 show_usage
 exit 1
end

Executing a Plugin

Now, let us learn how to run a plugin. Once we have coded a plugin, we can execute it using the knife command. 

The knife command triggers the run method, and the code inside the run gets executed.

module PluginExample

 class DemoPlugin < Chef::Knife
   def run
     puts "This is a demo plugin!"
   end
 end
end

 

Now, in the command line, if you type, knife demo plugin,

Then, you will get This is a demo plugin! as the output.

Suppose someone already creates the plugin that you need. So, instead of coding the plugin again, you can simply install the plugin file. All you have to do is copy the file to the directory inside the plugin. Ensure the file is coded in rugby and has .rb as an extension. You may also install the file from RubyGems.

Congratulations Ninja! You have learned all the concepts and syntax to create your custom knife plugin or install it from scratch. 

One last thing, ninja. Whenever we code a program, there is always a chance of getting errors or unexpected results. Troubleshooting these exceptions is essential but easy. Since we code plugins in Ruby, we handle the exceptions as we do for other codes written in Ruby. The troubleshooting available for a knife is mostly enough for managing the plugin exceptions.

Frequently Asked Questions

What is a recipe in Chef?

A recipe is a group of attributes that are used to manage the Chef’s infrastructure. Recipes load whenever we run the Chef client. Recipes modify or set the state of the infrastructure.

What do you mean by a Cookbook in chef?

As the name suggests, a Cookbook is a directory of recipes. It makes sure to provide the required infrastructure for the recipes present inside it.

How was Chef started?

Adam Jacob created Chef. He made it a tool for his consulting-based company. Initially, it started with the name “marionette.” Considering the long term, which was difficult to remember, Adam changed its name to Chef.

Conclusion

Hey Ninja! We hope you enjoyed reading this article and believe you can now create your own custom knife plugins per your requirements. Before ending this discussion, let's summarize everything we learned in this blog.

We started by understanding what a knife is and what its plugins are. We then looked at the syntax of a knife and understood each component one by one. We considered several examples while discussing. We also learned to install a plugin file in your knife. At last, we addressed the nature of exceptions and how to deal with them.

We ended our discussion with some frequently asked questions and their brief answers.

We sincerely hope the above discussion helped you to understand building custom knife plugins in Chef.

You can refer to our blogs on chef basicsintegrationinstallation, and settings if you wish to learn more about Chef and its various components.

Visit our website to read more such blogs. Make sure you enroll in our courses. You can take mock testssolve problems, and interview puzzles. Also, you can check out some exciting interview stuff- interview experiences and an interview bundle for placement preparations. Do upvote our blog to help fellow ninjas grow.

Keep Grinding! 🦾

Happy Coding! 💻

 

Live masterclass