Instructor: In our previous examples,
we used pre-built modules.
Now we're going to build a module ourselves.
Suppose you want to be able to look up
a computer-related acronym with a simple Linux command.
You can do this with the dict command,
but it won't work if you just install the dict package
in your Linux system.
You also need to install the dictd daemon
and the dict-vera acronym dictionary,
create a config file that tells the dict command
what dictionary to use, and start the dictd service.
This sequence of installing packages,
creating config files, and starting services
is such a common task for Puppet to perform
that it is known as the Package, File, Service pattern.
So it is extremely useful to learn
how to implement this pattern.
Puppet thinks of a host as a collection of resources.
Some examples of resources are the apache2 package,
the etc/motd file, the httpd daemon,
and a user account named fred.
A resource is essentially something that Puppet
can configure on a host.
Each resource belongs to a resource type.
For example, the apache package is a package,
the etc/motd file is a file,
the httpd daemon is a service,
and fred is a user.
Puppet has about 50 built-in resource types,
but the ones you will use the most
are packages, files and services.
It is also possible to create your own
custom resource types, but we're not going to cover that
in this course.
Before we start declaring resources
for our dictionary project,
we need to create a directory for our module.
First, we need to go to the modules directory.
Then we'll run the Puppet module generate command.
Remember that we want to use the skip interview option.
And we have to put the author and a hyphen
before the module name.
Let's call this module dict.
Okay, let's edit the init.pp manifest.
Now we'll scroll past the comments
and start writing our code after the opening line
of the class definition.
The first thing we need to do
is tell Puppet to install the three dict packages.
Let's start with the dict package.
First we put in the resource type,
which in this case is package.
Then we put in a curly brace
and the name of the package in quotes,
which is referred to as the title of the resource,
followed by a colon.
On the next line, we need to set the attributes
of this resource.
That is, we need to tell Puppet what to do
with this resource.
What we want is for this package to be installed.
The way we do that is to use the ensure attribute.
We say ensure followed by a hash rocket,
followed by what state we want this package to be in,
which is installed.
Finally, we need to put a closing brace on the next line.
What this package declaration tells Puppet
is that we want the dict package to be installed.
If it's not, then install it.
If it's already installed, then don't do anything.
In other words, ensure that this package is installed.
The Puppet language is different
from a typical programming language,
because instead of coding how to do something,
we code the state that we want the resource to be in
and Puppet makes it happen.
By the way, did you notice we used the same syntax
when we declared this package resource
as we did when we declared the motd class?
That's because we were using the resource syntax
to declare the motd class.
Before we write anymore code,
let's test what we've written so far.
First we'll save the file.
Before we test it, let's make sure that the dict package
isn't already installed.
We'll use the dpkg -l command.
And pipe it to grep dict.
Okay, it didn't find anything, so it's not installed.
Now we'll run Puppet apply.
Remember that we have to run this on the init.pp file
in the examples directory, not the one we just edited
in the manifest directory.
It says that it created the dict package.
Let's double check.
Yes, it's installed now.
Okay, let's add the other two packages.
We could just add two more package declarations
like the one we did for dict, but there's a faster way.
This is kind of an advanced feature I'm going to show you.
If you try to put more than one title,
that is, package name in this case,
in your resource declaration,
then you'll get a syntax error.
But Puppet will let you do it
if you put the strings in an array.
The syntax for an array is straightforward, too.
You just put square brackets around the list
and separate the strings with commas, like this.
We'll add dictd
and dict-vera,
and a closing square bracket.
That's all you have to do to install all three packages.
Let's save the file and test it out.
It worked.
Now, taking care of the config file
will take a bit more work
because we need to create the file
and put it somewhere first.
The standard place to put static files
is in a directory called files.
The Puppet module generate command
doesn't create a files directory,
so we'll have to create one.
The config file for dict is,
not surprisingly, called dict.conf.
So, let's create that in the files directory.
The configuration for dict is very simple.
You just need to give it a list of dictionary servers.
We're only going to put local hosts in our list,
so we just need to type server localhost.
Now let's edit the main manifest again
and add the dict.conf file.
To declare a file resource, you just type file,
then a curly brace,
then a title, which in this case is etc/dictd/dict.conf.
And remember to put the file name in quotes.
Then add a colon.
On the next line we need to tell it
where to get the file from.
The attribute name for that is source.
Then you put a hash rocket
and the location of the source file.
When you've stored your source file
in a module directory like we've done,
then you need to specify the path
with a special Puppet notation.
First we start with a quote, and then the word Puppet
and a colon, then we use three slashes, not two,
then we put modules/ and then the name of the module,
in this case dict.
Slash, and then the name of the file and a closing quote.
You'll notice that this is not the path name to the file,
even though it looks like it.
Because it doesn't include the files directory.
If you did include the files directory
between dict and dict.conf, then it won't work.
Finally, we put a closing brace on the next line.
Let's give it a try.
We'll save the file and run Puppet apply again.
You can see that it created the dict.conf file
and put some content in it.
Let's make sure that I put the right content in.
Yes, it worked.
Okay, we're almost done.
The final step is to declare the dictd service.
By now you can probably guess what the first line should be.
Go on, take a guess.
Did you say that it should have service,
and then a curly brace, and then the service name in quotes,
and then a colon?
If you did, then you're right.
The attribute we need to use on the next line
is also pretty predictable.
It's ensure, just like we used for the packages.
There is a difference, though.
We're going to set it to a different value.
For the packages, we set it to the value installed.
For a service, that doesn't make sense.
We're going to set it to the value running.
Now we just put in the closing brace, and we're done.
Now we'll run Puppet apply again.
Hmm, it didn't seem to do anything.
Well, that's because when we installed the dictd package,
it also started the dictd daemon.
To fully test our new code,
we need to stop the service first.
Now, let's run Puppet apply again
Great, as you can see, it changed the state
of the dictd service from stopped to running.
Let's just double check.
Yes, it is indeed running.
Now that the code is working, let's add it
to the node definition in the site.pp file
so it will run whenever we do a full Puppet run.
We just need to include dict in the node definition.
Before we test it, let's stop the dictd service again
so Puppet will have something to do.
And to make sure the motd setup module still works,
let's delete the motd file, too.
Okay, now let's do a full Puppet run.
We're using Puppet agent -t instead of Puppet apply.
Great, it created the motd file,
and it started the dictd service.
Hey, I just realized that we haven't actually used
the dict command to make sure it works.
Hmm, what acronym should we look up to test it?
I seem to recall that EMACS is an acronym,
so let's try that.
Hmm, "Eight Megabytes and Constantly Swapping."
Somehow I don't think that's what it really stands for.
Oh, I guess it actually stands for "Editing MACros."
Well, that might be the real one,
but I kinda like the first one better.
No offense to all you EMACS fans out there,
I like EMACS too.
And now, I think it's time to end this lesson.