Writing Plans
Difficulty: Intermediate
Time: Approximately 10 minutes
In this exercise you will discover Bolt Plans and how to run them with Bolt.
Prerequisites
Complete the following before you start this lesson:
About Plans
Use plans when you want to run several tasks or commands together on one or more targets. For instance to remove a target from a load balancer before you deploy the new version of the application, or to clear a cache after you re-index a search engine.
You can link a set of commands, scripts, and tasks together, and add parameters to them so they are easy to reuse. While you write plans in the Puppet language, you don’t need to install Puppet to use them.
Inspect Installed Plans
Bolt is packaged with useful modules and plan content. Run the bolt plan show
command to view a list of the plans installed in the project directory.
bolt plan show
The result:
aggregate::count
aggregate::targets
canary
facts
facts::info
puppetdb_fact
reboot
MODULEPATH:
/project/Boltdir/modules:/project/Boltdir/site-modules:/project/Boltdir/site
Use bolt plan show <plan-name> to view details and parameters for a specific plan.
Write a Plan Using run_command
Create a simple plan that runs a command on a list of targets.
Save the following as Boltdir/site-modules/exercise7/plans/command.pp
:
plan exercise7::command (TargetSpec $targets) {
return run_command("uptime", $targets)
}
Run the plan:
bolt plan run exercise7::command targets=target1
The result:
Starting: command 'uptime' on target1
Finished: command 'uptime' with 0 failures in 0.45 sec
Plan completed successfully with no result
Note:
targets
is passed as a parameter like any other, rather than a flag. This makes plans flexible when it comes to taking lists of different types of targets. You can still pass the names of groups in the inventory file to this parameter.Use the
TargetSpec
type to denote targets; it allows passing a single string describing a target URI or a comma-separated list of strings as supported by the--targets
argument to other commands. It also accepts an array of Targets, as resolved by calling theget_targets
method. You can iterate over Targets without needing to do your own string splitting, or as resolved from a group in an inventory file.
Write a Plan Using run_task
Create a task and then create a plan that uses the task.
Save the following task as Boltdir/site-modules/exercise7/tasks/write.sh
. The task accepts a filename and some content and saves a file to /tmp
.
#!/bin/sh
if [ -z "$PT_content" ]; then
echo "Need to pass content"
exit 1
fi
if [ -z "$PT_filename" ]; then
echo "Need to pass a filename"
exit 1
fi
echo $PT_content > "/tmp/${PT_filename}"
Run the task directly with the following command:
bolt task run exercise7::write filename=hello content=world --targets target1 --debug
In this case the task doesn’t output anything to stdout. It can be useful to trace the running of the task, and for that the --debug
flag is useful. Here is the output when run with debug:
Did not find config for target1 in inventory
Started with 100 max thread(s)
ModuleLoader: module 'boltlib' has unknown dependencies - it will have all other modules visible
Did not find config for target1 in inventory
Starting: task exercise7::write on target1
Authentication method 'gssapi-with-mic' is not available
Running task exercise7::write with '{"filename":"hello", "content":"world"}' via on ["target1"]
Started on target1...
Running task run 'Task({'name' => 'exercise7::write', 'implementations' => [{'name' => 'write.sh', 'path' => '/Users/username/puppetlabs/tasks-hands-on-lab/07-writing-plans/modules/exercise7/tasks/write.sh', 'requirements' => []}], 'input_method' => undef})' on target1
Opened session
Executing: mktemp -d
stdout: /tmp/tmp.mJo9THENdL
Command returned successfully
Executing: chmod u\+x /tmp/tmp.mJo9THENdL/write.sh
Command returned successfully
Executing: PT_filename=hello PT_content=world /tmp/tmp.mJo9THENdL/write.sh
Command returned successfully
Executing: rm -rf /tmp/tmp.mJo9THENdL
Command returned successfully
Closed session
Finished on target1:
{"target":"target1","status":"success","result":{"_output":""}}
{
}
Finished: task exercise7::write with 0 failures in 0.89 sec
Successful on 1 target: target1
Ran on 1 target in 0.97 seconds
Write a plan that uses the task you created. Save the following as Boltdir/site-modules/exercise7/plans/writeread.pp
:
plan exercise7::writeread (
TargetSpec $targets,
String $filename,
String $content = 'Hello',
) {
run_task(
'exercise7::write',
$targets,
filename => $filename,
content => $content,
)
return run_command("cat /tmp/${filename}", $targets)
}
The plan takes three arguments, one of which (content
) has a default value. We’ll see shortly how Bolt uses that to validate user input.
First, the plan runs the exercise7::write
task from above, setting the arguments for the task to the values passed to the plan. This writes out a file in the /tmp
directory. Next, the plan runs a command directly, in this case to output the content written to the file in the above task.
Run the plan using the following command:
bolt plan run exercise7::writeread filename=hello content=world targets=target1
The result:
Starting: task exercise7::write on target1
Finished: task exercise7::write with 0 failures in 0.88 sec
Starting: command 'cat /tmp/hello' on target1
Finished: command 'cat /tmp/hello' with 0 failures in 0.41 sec
Plan completed successfully with no result
Since content
is optional you can choose not to pass a value to it, in which case the default value will be assigned. Lastly, when running multiple steps in a plan only the last step will generate output.
Next Steps
Now that you know how to create and run basic plans with Bolt you can move on to: