Starting a Job on a Code Commit in Jenkins

Where We Left Off with Jenkins Last Time

In my previous post, we left off with installing Jenkins (which was just starting a Docker container) and making a job that printed out “Hello World”.  In this post, we are going to make that job get kicked off automatically when code gets committed.  In order to do that, we are going to need a source code repository.

Rather than assume you have a source code repository at your workplace, I’m going to create what I’ll call a “DevOps playground”.  Something that will have all the necessary services we need all in one defined location.  In order to do this, we are going to explore launching multiple Docker containers at once using docker-compose.  All the tools we need for this entire experiment will only require a computer with Docker installed!

Playground

What is Docker Compose?

Docker compose is a way of launching multiple Docker containers all at once with an easy command.  Rather than doing something like “docker run jenkins” and “docker run git” (and all the options you need for those), we will have a docker-compose.yml file that will define the containers to run and their options.  We will then run something like “docker-compose up” to launch them all, and “docker-compose down” to stop them all (or something similar).

Investigating the docker-compose.yml File

To get the code for this lesson, clone https://github.com/tscott8706/devops-playground.

Open up the docker-compose.yml file.  You will see three different sections:

  • services: these are the Docker containers that will be launched.
  • volumes: these are for persistent storage (making sure our Jenkins jobs and Git repos don’t go away when we restart the containers)
  • network: this is to create a custom network so our containers can have static IP addresses.

Each service defines some of the command-line parameters you would give “docker run” if you started the container with docker run.  Instead, these parameters can live inside this docker-compose file.

  • image: the Docker image that will be launched.
  • restart: “always” makes our containers restart after a reboot, after the container crashes… and for basically all the other situations too.
  • ports: mapping the port within the container to the host.
  • networks: the network to use and the IP address to give to that container.
  • environment: environment variables to set once the container comes up.
  • volumes: persistent storage for containers.  These use Docker volumes, but you could also map these to a directory location on the host.

In order to start these containers, change to the devops_playground directory (the one with the docker-compose.yml file in it) and run “docker-compose up -d” (the -d means detached mode, which hides the output and keeps the containers running after the terminal closes).  The first time you run docker-compose, it may download the images from the Internet.

Docker Compose

Once it finishes downloading, you will see it start the containers.

Docker Compose Up

For your information, you can use docker-compose down to stop those containers.

We now have our playground up and running!  Now let’s explore Jenkins.

Making Jenkins Job Run After a Commit

If you have not created a job yet, look at the “Your First Jenkins Job” section in my previous post on Jenkins.  We are going to expand on that job.  But first, let’s create our Git Server repo.

You can view more about the Git Server Docker container here, but I’ll outline the steps we need to take.  (This assumes the host is on a Linux-like environment, so modify the commands if needed).  This puts SSH keys on the server then creates and uploads the repo.

  • ssh-keygen -t rsa
  • docker cp ~/.ssh/id_rsa.pub git-server:/git-server/keys
  • docker exec devopsplayground_jenkins_1 mkdir /var/jenkins_home/.ssh
  • docker cp ~/.ssh/id_rsa devopsplayground_jenkins_1:/var/jenkins_home/.ssh (copy that key into the Jenkins container)
  • docker-compose down
  • docker-compose up -d
  • Verify that worked:
    ssh git@10.10.10.2 -p 22 (should show a welcome message)
  • Now ssh from the Jenkins container:
    • docker exec -it devopsplayground_jenkins_1 bash
    • ssh git@10.10.10.2
    • yes
    • exit
  • mkdir examplerepo
  • cd examplerepo
  • Make a file called test.txt and put “hello” into it.
  • git init –shared=true
  • git add .
  • git commit -m “my first commit”
  • cd ..
  • git clone –bare examplerepo examplerepo.git
  • docker cp examplerepo.git git-server:/git-server/repos
  • docker exec git-server sh -c “cd /git-server/repos && chown -R git:git . && chmod -R ug+rwX . && find . -type d -exec chmod g+s ‘{}’ +”
    • This is needed because of file and directory ownership.
    • I could not get the scp command to work, which I assume would have given the directory the proper git user ownership, but I’m unsure.  As it stands, every time a new repo is copied to the server, this exec command should be run to fix permissions.
  • rm -rf examplerepo.git
  • rm -rf examplerepo
  • git clone ssh://git@10.10.10.2/git-server/repos/examplerepo.git
    • If this worked, your git repo is working.

Git Clone

Back to Jenkins.

  • Click the Example Job project, then click Configure to go to that job’s configuration.  (It will be at URL http://localhost:8080/job/Example%20Job/configure)
  • Under the Source Code management section, select Git.
    • Repo URL is ssh://git@10.10.10.2/git-server/repos/examplerepo.git
    • For the branch specifier, put */master

Source Code Management

  • Check the Poll SCM checkbox under the build triggers.  Set it to check every minute: * * * * *
  • Change the execute shell build command (the one that echos Hello World) to perform an ls and cat test.txt command.  So it should say:
    ls
    cat test.txt
  • Save the job.

Wait for about a minute, then you should notice the job automatically gets kicked off (that’s because it has never run it against that repo, so there was a change since the last time it ran… which was never).

Awesome!  Now we automatically have a job running when we change something in our code repo!  Let’s look at that specific run of the job.  Click the latest item in the Build History (it’s in the middle or lower-left of the Example Job project page).  If it worked, it should be a blue ball – red means it failed.

Build HistoryOnce you get to the build history page, click on the Console Output link on the left to see the results of the build.  It shows all the commands that were run.  For example, you can see that the ‘ls’ command (test.txt) and the ‘cat’ command (hello).  You can also see the commits that occurred and revision.

Console Output

Make a Commit and Watch the Job Run

Let’s try one more thing.  We will change the text inside test.txt to goodbye then commit that change.

  • Change hello to goodbye inside test.txt.
  • git add test.txt
  • git commit -m “change text to goodbye”
  • git push origin master

Let’s see what happens in Jenkins.  It should automatically build again.  Go to the latest build history item again.

Change Log

Now we see the changes that occurred since the last build.  This is SUCH a big deal for testing purposes.  Suppose your job does something useful (we will go into this in future posts) such as building code and testing the result.  If something breaks, we know the changes that broke it are the ones that it reports on this page (assuming you didn’t let code stay broken after a previous commit).  Very useful!

If you go into the console output, you will also see that the cat command now outputs goodbye rather than hello.

Conclusion

This lesson is a very big piece of continuous integration.  If you don’t understand something, read over it and try to understand it.  Go through these steps on your own.   Play around and see if you can make this job do other things.

The next lessons will make this job much more useful.  We will cover…

  • Building code
  • Testing code
  • Reporting test results
  • Running static analysis checks
  • Preparing our code for deployment

Things are about to get a lot more useful!

Leave a Reply

Your email address will not be published. Required fields are marked *