Narrator: Let's say that you want to set the contents
of the /etc/motd file to "Hello world!" instead of
the default content set by the motd module.
To do that, we're going to create our own module
that uses the motd module.
A module contains manifests, which are just files
with Puppet code in them.
Inside the manifests are a variety of elements,
but the most important ones are classes.
A class is a named block of Puppet code
that can be applied when it is invoked by its name.
A very simple module could contain just one manifest file
with one class inside it.
A more complex module could contain a whole hierarchy
of manifests with numerous classes
and even supporting libraries.
In this motd example, we'll be creating
a very simple module.
Before we create our own module,
let's see what's in the motd module.
The main modules directory for Puppet is here.
Yes, that's a pretty long path name,
but the directory is so deep because it's possible
to have multiple modules directories
for multiple environments,
such as development and production.
We're only using the production environment
because this is just a test VM
that's not hooked up to a real production environment.
Have a look at what's in the motd directory
with the ls -R command,
so we can see all of the subdirectories too.
Wow, that's a lot of files.
To make it a bit easier to read,
I like to use the tree command which, unfortunately,
isn't installed by default on Ubuntu,
but it's very easy to install, so let's do that.
Now just type tree motd.
That's a lot nicer, isn't it?
This is a much more complex module
than we're going to create, so I won't go
into a lot of detail about it.
But note that the heart of the module
is the manifests directory, which only contains
one manifest called init.pp.
By the way, all Puppet manifests have the extension pp.
The biggest directories are spec and tests,
which are both related to testing the module's code,
rather than containing module code themselves.
To create our own module, we need to create
a directory structure like this,
although quite a bit simpler.
Thankfully, Puppet provides a nice tool for doing that
called puppet module generate.
We're just going to add one option to it
called --skip-interview.
If you don't include that option,
then it will ask you a whole bunch of questions
to fill in the metadata for the module.
We're going to skip that.
Then we just tell it the name of the module we're creating.
The name needs to be two words with a hyphen in between,
just like puppetlabs-motd.
The first word is the author and the second
is the name of the module.
For an author name, I'm just going to use test for now
because I'm not planning on publishing
this module on the forge.
For the module name, we can't call it motd
because that's already taken.
So let's call it motdsetup.
Makes sure you're still in the modules directory
before you run this command, because it will create
the module in your current directory.
Let's have a look at what it created
using the tree command again.
There are still quite a few files and directories,
but it's much simpler than what we saw in the motd module.
Even so, we're going to ignore everything in here
except for two files, both of them named init.pp.
First we'll edit the init.pp file in the manifest directory
because this is the main manifest file in a module.
There is a large comment section in here
that you would fill in to document your module,
but we're going to leave that alone.
If you scroll down, you'll see that,
after the comment section, there is the beginning
of a class definition for motdsetup.
That's because the name of the class
in the init.pp manifest has to be the same
as the name of the module.
By the way, you might be wondering
what language Puppet uses.
Well, Puppet has its own language, although it was inspired
by the Nagios configuration file format.
It's very similar to many other languages though,
so it's pretty easy to learn.
Now we just need to add some code
to use the puppetlabs-motd module
and to set the contents of the motd file to Hello World.
The way we do that is through a class declaration.
This is different from a class definition,
which is kind of confusing.
Let's put in the code to declare the motd class,
and then I'll explain definitions versus declarations.
If you look in the example section in the comments above,
it shows an example of declaring a class.
First you type class, and then a curvy brace.
Then you type the name of the class you want to use
surrounded by single quotes and follow it with a colon.
In this case, we want to use motd,
which is the main class of the motd module.
Don't type motdsetup like they show in the example,
because that's the name of the class we're creating,
not the name of the puppetlabs-motd module's class.
On the next line, you specify a parameter
that you want to set.
In our case, we want to set the parameter
that says what content to put in the /etc/motd file.
The name of that parameter is content.
How do I know that?
Because it's in the documentation for the motd module.
Hey, did you notice that the documentation also gives us
an example of how to set the content parameter?
Coincidentally, that's exactly what we need
to accomplish our goal.
So let's just copy this line.
We need to paste it here.
After the content parameter, you use a hash rocket.
I would just call it an arrow,
but hash rocket sounds cooler, doesn't it?
Then you put in the value
we're setting the content parameter to,
which in this case is "Hello World!" in double quotes.
After that, you can put in a comma,
which would be necessary if we were going to
set more parameters, but since we're not going to do that,
we don't need the comma.
However, it's a common practice to put in a comma anyway,
just in case you add another parameter later
and you forget to add the comma at that time.
Now, we need to make one change to this.
The /n should actually be a \n.
\n is a special character that means new line.
If we were to use /n, then it wouldn't be interpreted
as a special character, and our Hello World message
would have a messy looking /n at the end of it.
So the moral of the story is,
don't blindly trust everything that you read
in a module's documentation.
Anyway, back to the task at hand.
The final thing we need to do
is add a closing curly brace on the next line.
Now I'll explain the difference
between class definitions and declarations.
When we define a class, we are saying
what the class does when it is run.
In this case, we are defining class motdsetup,
and what it inside the curly braces
is what this class does when you run it.
When we declare a class, that's when
we're actually running it.
In this case, we're running the motd class
and giving it a parameter to use during that run.
The easy way to see whether a class is being defined
or being declared is to look at where the class name is.
When you're defining a class, you put the class name
before the curly brace.
When you're declaring a class, you put the class name
after the curly brace.
Okay, now that we have that straight, let's save the file.
If you want to make sure that you don't have
any syntax errors in your code,
then you can run the Puppet parser like this,
puppet parser validate init.pp.
If everything is okay, then there won't be any output.
This is a bit different from running puppet apply
with the noop option, because the Puppet parser
only checks the syntax.
It doesn't tell you what would happen
if Puppet were to run this code.
Just so you can see what it would say
if you did have a syntax error,
let's go back in and mess something up.
Suppose we forgot to put quotes around Hello World.
Let's remove them and see what happens.
We'll run the Puppet parser again.
Now it tells us we have a syntax error at World.
It also tells us the line and column number
where the error occurs, that is line 47, column 22.
Let's go back in and fix the error.
If you're using vi, then you can type 47G,
and it will take you to line 47.
If we put our cursor over the W in World,
then you'll see, at the bottom right,
it shows that the line number is 47
and the column number is 22.
You might be wondering why it says
the syntax error starts at World instead of at Hello.
That's because the parser doesn't know
that Hello World is supposed to be a string.
It's just interpreting Hello and World
as two separate values, and it knows you can't assign
more than one value to a parameter.
If you just put Hello in without any quotes,
then the parser would not say there's an error.
This is an example of how the parser can say
that the syntax is okay, but the code will still
not execute properly because, of course,
code can technically have the correct syntax
and still not do what you intended.
Okay, now we'll add the quotes back in and save the file.
We'll run Puppet parser one more time just to make sure.
There's no output, so it's all good again.
Now, you might think that we could run puppet apply
to test out our code, but that actually wouldn't work.
In our init.pp manifest, we just defined
our motdsetup class.
In order to run it, we need to declare it somewhere.
So puppet apply would run without errors,
but it wouldn't do anything.
Luckily, the puppet module generate command
that we ran to create our module's directory structure
also created a file that declares our motdsetup class.
It's in a directory called examples.
And it's also called init.pp.
Let's have a look at what's inside.
You can see that, after a bunch of comments, it only has
one real line of code.
That line is include ::motdsetup.
This is another way of declaring a class.
You'll recall that when we declared the motd class
in our motdsetup definition,
we used class curly brace motd,
which looked totally different from the line in this file.
The reason we didn't use include to declare the motd class
is because we also needed to set the content parameter.
Here we just need to declare the class,
and we don't need to set any parameters.
This is a much simpler syntax,
but it's still a bit weird because
it has two colons in front of the module name.
The truth is that you don't need to use the two colons.
In fact, you could remove them
and everything would still work.
In versions of Puppet before version four,
it was sometimes necessary to put in the double colons
due to a design problem, but that has been fixed
in version four, so you don't need to do that anymore.
However, there isn't any harm in leaving them there,
so we won't bother editing this file.
Okay, so to test our motdsetup class,
we need to run puppet apply on this file,
which will declare the motdsetup class.
By the way, you might get a warning message
about the hiera.yaml file.
If you do, then just ignore it.
It looks like it worked, but let's see
what's in the /etc/motd file now, just to make sure.
Excellent, and that's it for this lesson.