2016 Blogs, Blog

Using Bootstrap Template to Copy Files to Node

Abstract

During Bootstrap Chef executes systems calls on remote machines, which includes preparing file system required for chef, installing chef-client and executing chef-client. Bootstrap template is being used to execute all commands on remote machine in order to bring the machine in desired state as a Chef node. Default template is shipped with Chef but it has a provision to use customized template for bootstrapping. So in situations where some additional configuration is required to be done on all nodes, bootstrap templates can be used.

Introduction

Bootstrapping is the process in Chef to register a node with Chef Server from workstation. Typical process of bootstrapping includes creating “validation.pem” and “client.rb”, installing chef-client on remote node etc. and then run a chef-client. First chef-client always look for validation, if available, registers node on Chef Server and gets the “client.pem” which is the authentication key of node for further chef-client run.

Consider a scenario where some set of files needs to be copied over the bootstrapped node. There are many ways to achieve this state on node like doing a copy using rsync, scp or mounting file system or transferring using HTTP etc. etc.

For example, if multiple secret keys for data bag encryption are being used, in that case all the keys needs be transferred on each node in order to decrypt the data bag item values in recipes. So having all the keys becomes the part of desired state of node, and it can be achieved with the bootstrap process of node without using any additional service like rsyc, scp, mounting etc…

Assumptions and Prerequisites

This walkthrough describes bootstrapping a Linux operating system and makes the following assumptions about your familiarity with Linux, Ruby and Chef:

·         You have the workstation configured with your Chef Server details

·         You know enough about the usage of knife commands

·         You have one Linux machine to bootstrap

·         You know enough about Linux to create directories and files

·         You know enough about Ruby to use variables, libraries and IO class

·         You have the basic knowledge of writing erb templates

Step 1: Preparing the Custom Template

Chef usage the default template named “chef-full.erb”, which can be found under chef embedded gems directory. Typical path for the default template on windows workstation is “C:opscodechefembeddedlibrubygems<ruby-version>gemschef-<chef-client-version>libchefknifebootstrap” and on Linux workstation is “/opt/opscode /chef/embedded/lib/ruby/gems/<ruby-version>/gems/chef-<chef-client-version>/lib/chef/knife/bootstrap”.

Below is the content of “chef-full.erb” template

bash -c ’

<%= “export https_proxy=”#{knife_config -%>

distro=`uname -s`

if test “x$distro” = “xSunOS”; then

 if test -d “/usr/sfw/bin”; then

   PATH=/usr/sfw/bin:$PATH

   export PATH

 fi

fi

exists() {

 if command -v $1 &>/dev/null

 then

   return 0

 else

   return 1

 fi

}

install_sh=“https://www.opscode.com/chef/install.sh”

version_string=“-v <%= chef_version %>”

if ! exists /usr/bin/chef-client; then

 echo “Installing Chef Client…”

 if exists wget; then

   bash <(wget <%= “–proxy=on ” if knife_config}” ” if knife_config = “chef-full-custom”

Step 3: Modifying the Custom Template

Now let’s modify the template and write the code to transfer the files from workstation to node during bootstrap process.

As explained earlier, bootstrap template is a collection of system commands and it’s an erb file so we can write Ruby code in it.

Create a directory on target node

We can write commands to be executed on Linux system directly in a template. So here to create a directory we can use mkdir command in template. We have specified variable in knife.rb called target_dir which defines the directory to be created on target node. In order to use this variable we need to use Chef::Config class defined with chef gem.

Chef::Config

Read the files from workstation and create on target node

We have directory create on target node, now we need to transfer the files from source directory on workstation to target directory.

The approach here will be reading all the files using IO class (Input/output) of ruby from source dir and attaching the read content with the command to create file.

To read the file using IO class, the syntax will be as below:

IO.read(File.expand_path(<File_Name>))

On Linux system to create a file using cat command along with the content of file being passed in-line. And content will be provided by reading the file as per above command. Syntax for this will be as below:

cat >  #{Chef::Config}”), “*.{pem}”)).each do |file|

             @content << “cat >  #{Chef::Config}”), “*.{pem,rb}”))

Above line from snippet provides an array of all the file name with .pem and .rb extention under source_dir, then we iterate over the array and read files one by one and assign the create file command to “content” variable.

After this we are set with all the commands and now we need to execute. The code will be as below:

<%= @content %>

Above command will end up in creating files on target node under target_dir directory.

Now add this code in the template before the chef-client execution.  Then template should look like below:

bash -c ’

<%= “export https_proxy=”#{knife_config -%>

.

.

.

cat > /etc/chef/first-boot.json <<‘EOP’

<%= first_boot.to_json %>

EOP

mkdir -p Chef::Config}”), “*.{pem}”)).each do |file|

             @content << “cat >  #{Chef::Config}/#{File.basename(file)} <<‘EOP’n” +

                        IO.read(File.expand_path(file)) + “nEOPn”

           end

-%>

<%= @content %>

echo “Starting first Chef Client run…”

<%= start_chef %>’

 

Conclusion

Now with this change every time when you bootstrap a node, that node will have a directory with all the files specified as per code. The code again can be modified in order to choose specific extension of files, create multiple directories etc…

Author


Avatar