Initial commit

This commit is contained in:
Benjamin Muschko
2019-11-17 16:40:48 -07:00
commit 248499c8f6
108 changed files with 994 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

0
README.md Normal file
View File

View File

@@ -0,0 +1,15 @@
# Exercise 1
In this exercise, you will practice installing and configuring Jenkins on your machine.
## Installing Jenkins
1. Download the [latest stable Jenkins WAR file](http://mirrors.jenkins.io/war-stable/latest/jenkins.war). To download from the command line, use `wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war`.
2. Open a terminal and navigate to the directory containing the WAR file.
3. Run the command `java -jar jenkins.war` to start Jenkins on port 8080. Provide the `--httpPort` option if you experience a port conflict e.g. `java -jar jenkins.war --httpPort=9999`. After a couple of seconds you should see the message "Jenkins is fully up and running" in the log output.
4. Open a browser of your choice and navigate to `localhost:<port>`. The default is `localhost:8080`.
5. In the screen named "Unlock Jenkins", enter the password from the console output. Press the "Continue" button.
6. In the screen named "Customize Jenkins", select the option "Install suggested plugins".
7. In the screen named "Create First Admin User", enter all fields with credentials for your Jenkins administrator. Press the "Save and Continue" button.
8. In the screen named "Instance Configuration", keep the default value. Press the "Save and Finish" button.
9. You should see the message "Jenkins is ready!". Press the "Start using Jenkins" button. You'll be redirected to the Jenkins dashboard.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -0,0 +1,145 @@
# Solution
Download the WAR file with the help of `wget`.
```bash
wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war
--2019-08-09 16:06:23-- http://mirrors.jenkins.io/war-stable/latest/jenkins.war
Resolving mirrors.jenkins.io (mirrors.jenkins.io)... 52.202.51.185
Connecting to mirrors.jenkins.io (mirrors.jenkins.io)|52.202.51.185|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://mirror.xmission.com/jenkins/war-stable/2.176.2/jenkins.war [following]
--2019-08-09 16:06:23-- http://mirror.xmission.com/jenkins/war-stable/2.176.2/jenkins.war
Resolving mirror.xmission.com (mirror.xmission.com)... 2607:fa18:0:3::13, 198.60.22.13
Connecting to mirror.xmission.com (mirror.xmission.com)|2607:fa18:0:3::13|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 77379386 (74M) [application/java-archive]
Saving to: jenkins.war
jenkins.war 100%[=================================================>] 73.79M 6.46MB/s in 14s
2019-08-09 16:06:37 (5.36 MB/s) - jenkins.war saved [77379386/77379386]
```
First, start the Jenkins server. Copy the password printed in the log output to the clipboard.
```bash
$ java -jar jenkins.war
Running from: /Users/bmuschko/dev/tools/jenkins-2.150.1/jenkins.war
webroot: $user.home/.jenkins
Jul 16, 2019 4:59:30 PM org.eclipse.jetty.util.log.Log initialized
INFO: Logging initialized @501ms to org.eclipse.jetty.util.log.JavaUtilLog
Jul 16, 2019 4:59:30 PM winstone.Logger logInternal
INFO: Beginning extraction from war file
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.handler.ContextHandler setContextPath
WARNING: Empty contextPath
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.Server doStart
INFO: jetty-9.4.z-SNAPSHOT; built: 2019-02-15T16:53:49.381Z; git: eb70b240169fcf1abbd86af36482d1c49826fa0b; jvm 1.8.0_181-b13
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.webapp.StandardDescriptorProcessor visitServlet
INFO: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.DefaultSessionIdManager doStart
INFO: DefaultSessionIdManager workerName=node0
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.DefaultSessionIdManager doStart
INFO: No SessionScavenger set, using defaults
Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.HouseKeeper startScavenging
INFO: node0 Scavenging every 660000ms
Jenkins home directory: /Users/bmuschko/.jenkins found at: $user.home/.jenkins
Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.handler.ContextHandler doStart
INFO: Started w.@21ec5d87{Jenkins v2.176.1,/,file:///Users/bmuschko/.jenkins/war/,AVAILABLE}{/Users/bmuschko/.jenkins/war}
Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.AbstractConnector doStart
INFO: Started ServerConnector@388ffbc2{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.Server doStart
INFO: Started @4486ms
Jul 16, 2019 4:59:34 PM winstone.Logger logInternal
INFO: Winstone Servlet Engine v4.0 running: controlPort=disabled
Jul 16, 2019 4:59:35 PM jenkins.InitReactorRunner$1 onAttained
INFO: Started initialization
Jul 16, 2019 4:59:35 PM jenkins.InitReactorRunner$1 onAttained
INFO: Listed all plugins
Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained
INFO: Prepared all plugins
Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained
INFO: Started all plugins
Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained
INFO: Augmented all extensions
Jul 16, 2019 4:59:37 PM jenkins.InitReactorRunner$1 onAttained
INFO: Loaded all jobs
Jul 16, 2019 4:59:37 PM hudson.model.AsyncPeriodicWork$1 run
INFO: Started Download metadata
Jul 16, 2019 4:59:37 PM hudson.util.Retrier start
INFO: Attempt #1 to do the action check updates server
Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.StaticWebApplicationContext@54fe6e54: display name [Root WebApplicationContext]; startup date [Tue Jul 16 16:59:38 MDT 2019]; root of context hierarchy
Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.StaticWebApplicationContext@54fe6e54]: org.springframework.beans.factory.support.DefaultListableBeanFactory@3e77b308
Jul 16, 2019 4:59:38 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3e77b308: defining beans [authenticationManager]; root of factory hierarchy
Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.StaticWebApplicationContext@2b22ca16: display name [Root WebApplicationContext]; startup date [Tue Jul 16 16:59:38 MDT 2019]; root of context hierarchy
Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.StaticWebApplicationContext@2b22ca16]: org.springframework.beans.factory.support.DefaultListableBeanFactory@28d4e2d4
Jul 16, 2019 4:59:38 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@28d4e2d4: defining beans [filter,legacy]; root of factory hierarchy
Jul 16, 2019 4:59:38 PM jenkins.install.SetupWizard init
INFO:
*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
34155b26571e4a2eb6eca68ddc4d5749
This may also be found at: /Users/bmuschko/.jenkins/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
Jul 16, 2019 4:59:42 PM hudson.model.UpdateSite updateData
INFO: Obtained the latest update center data file for UpdateSource default
Jul 16, 2019 4:59:42 PM hudson.model.UpdateSite updateData
INFO: Obtained the latest update center data file for UpdateSource default
Jul 16, 2019 4:59:43 PM jenkins.InitReactorRunner$1 onAttained
INFO: Completed initialization
Jul 16, 2019 4:59:43 PM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller
Jul 16, 2019 4:59:43 PM hudson.util.Retrier start
INFO: Performed the action check updates server successfully at the attempt #1
Jul 16, 2019 4:59:43 PM hudson.model.AsyncPeriodicWork$1 run
INFO: Finished Download metadata. 5,856 ms
Jul 16, 2019 4:59:43 PM hudson.UDPBroadcastThread run
INFO: Cannot listen to UDP port 33,848, skipping: java.net.SocketException: Can't assign requested address
Jul 16, 2019 4:59:43 PM hudson.WebAppMain$3 run
INFO: Jenkins is fully up and running
```
Paste the password from the clipboard into the password field.
![Unlock Jenkins](./images/unlock-jenkins.png)
Select the option to install recommended plugins.
![Customize Jenkins](./images/customize-jenkins.png)
The installation process will run for a couple of minutes.
![Plugin Installation Progress](./images/plugin-installation-progress.png)
Enter credentials for your first admin user.
![First Admin User](./images/first-admin-user.png)
Skip over the instance configuration step.
![Instance Configuration](./images/instance-configuration.png)
Jenkins indicates that it is ready for use.
![Jenkins Is Ready](./images/jenkins-ready.png)
The installation process is complete. You should land on the dashboard.
![Jenkins Dashboard](./images/jenkins-dashboard.png)

View File

@@ -0,0 +1,14 @@
# Exercise 2
In this exercise, you will create a new freestyle job and configure it to build with parameters. You'll also have a look at the Jenkins home directory to inspect the directory structure representing the job.
## Defining, Configuring and Organizing a Job
1. From the dashboard, click the "New Item" button.
2. Enter the item name "my-freestyle-job" and select "Freestyle project". Press the "OK" button.
3. In the job configuration, define the option to only keep the last 2 builds. Provide the description "A simple freestyle job". Upon building the project, a String parameter named `MESSAGE` should be provided. Press the "Save" button.
4. Trigger a new build by pressing the "Build with Parameters" button. Enter a value for the `MESSAGE` parameter. The build should finish successfully. Locate the provided parameter value in the build information.
5. Run the build two more times. What do you see?
6. Create a new view named "test". Add the job to the view.
7. Create a new folder named "freestyle" as part of the view. Move the job into the folder.
8. Locate the build information in `$JENKINS_HOME`. Inspect the directory structure.

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 KiB

View File

@@ -0,0 +1,66 @@
# Solution
We'll start by creating the new freestyle job.
![New Freestyle Job](./images/new-freestyle-job.png)
Configure the job as follows.
![Job Configuration](./images/job-configuration.png)
The build will ask for a parameter value when triggered.
![Build with Parameters](./images/build-with-params.png)
The build history only stores the previous two builds.
![Build History](./images/build-history.png)
Create a new view.
![New View](./images/new-view.png)
After adding the job to the view, it will show up in a separate tab.
![Job in View](./images/job-in-view.png)
Create a new folder.
![New Folder](./images/new-folder.png)
The job became a child of the folder after moving it there.
![Job In Folder](./images/job-in-folder.png)
Navigating to the `job` directory under the Jenkins Home reveals the build history.
```bash
$ cd /Users/bmuschko/.jenkins/jobs/freestyle/jobs
$ tree my-freestyle-job
my-freestyle-job
├── builds
│   ├── 1
│   │   ├── build.xml
│   │   ├── changelog.xml
│   │   └── log
│   ├── 2
│   │   ├── build.xml
│   │   ├── changelog.xml
│   │   └── log
│   ├── 3
│   │   ├── build.xml
│   │   ├── changelog.xml
│   │   └── log
│   ├── lastFailedBuild -> -1
│   ├── lastStableBuild -> 3
│   ├── lastSuccessfulBuild -> 3
│   ├── lastUnstableBuild -> -1
│   ├── lastUnsuccessfulBuild -> -1
│   └── legacyIds
├── config.xml
├── lastStable -> builds/lastStableBuild
├── lastSuccessful -> builds/lastSuccessfulBuild
└── nextBuildNumber
8 directories, 15 files
```

View File

@@ -0,0 +1,12 @@
# Exercise 3
In this exercise, you will configure the job to build based on a cron definition. Moreover, you will create downstream job that should be built automatically if the upstream job is successful.
## Configuring Build Triggers and Steps for a Job
1. Add a default value for the build parameter named `MESSAGE` e.g. `Hello World!`.
2. Create a build trigger that builds the project every minute.
3. Add a build step that executes the shell command `echo "Message: $MESSAGE"`. The message is value of the parameter.
4. After a minute the first execution should have been triggered. Check the log output of the build and find the rendered message.
5. Create another freestyle project named `downstream-job` in the same folder.
6. Configure the initial job to execute the `downstream-job` if it was stable.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 KiB

View File

@@ -0,0 +1,33 @@
# Solution
Modify the existing build parameter.
![String Parameter](./images/string-parameter.png)
Add the cron build trigger and the build step.
![Build Trigger And Step](./images/build-trigger-and-step.png)
The output render the interpolated value of the `echo` command.
```bash
Started by timer
Running as SYSTEM
Building in workspace /Users/bmuschko/.jenkins/workspace/freestyle/my-freestyle-job
[my-freestyle-job] $ /bin/sh -xe /var/folders/02/3dgzjkqj4kz0g7lnrk0w93c00000gn/T/jenkins3548490840940668236.sh
+ echo "Message: Hello World!"
Message: Hello World!
Finished: SUCCESS
```
Create a new job in the same folder.
![Both Jobs In Folder](./images/both-jobs-in-folder.png)
Configure the downstream job from the initial job.
![Downstream Job Configuration](./images/build-downstream.png)
The downstream job indicates the upstream job.
![Downstream Build Information](./images/executed-downstream-job.png)

View File

@@ -0,0 +1,10 @@
# Exercise 4
In this exercise, you will create a new job that pulls the source code from a repository on GitHub. Upon triggering a build, the job will execute a Gradle build.
## Configuring a GitHub Repository
1. Create a new freestyle job named `gradle-initializr`.
2. Configure Git as the SCM and use the repository URL `git@github.com:bmuschko/gradle-initializr.git`. Only build from the branch `master`.
3. Add a build step to run the Gradle command `clean build` using the Wrapper.
4. Trigger a build and look at the output.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,71 @@
# Solution
Create a new job.
![Freestyle Job](./images/new-job.png)
Configure the Git SCM and point the proper URL. The default is the `master` branch.
![Git SCM](./images/git-scm.png)
Create the Gradle build step.
![Git SCM](./images/gradle-build-step.png)
The build log should look similar to the output below.
```bash
Started by user Admin
Running as SYSTEM
Building in workspace /Users/bmuschko/.jenkins/workspace/gradle-initializr
No credentials specified
Cloning the remote Git repository
Cloning repository git@github.com:bmuschko/gradle-initializr.git
> git init /Users/bmuschko/.jenkins/workspace/gradle-initializr # timeout=10
Fetching upstream changes from git@github.com:bmuschko/gradle-initializr.git
> git --version # timeout=10
> git fetch --tags --force --progress git@github.com:bmuschko/gradle-initializr.git +refs/heads/*:refs/remotes/origin/*
> git config remote.origin.url git@github.com:bmuschko/gradle-initializr.git # timeout=10
> git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
> git config remote.origin.url git@github.com:bmuschko/gradle-initializr.git # timeout=10
Fetching upstream changes from git@github.com:bmuschko/gradle-initializr.git
> git fetch --tags --force --progress git@github.com:bmuschko/gradle-initializr.git +refs/heads/*:refs/remotes/origin/*
> git rev-parse refs/remotes/origin/master^{commit} # timeout=10
> git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision 8e725ea3507f5ac0f8251234e1ff0f214b228d3d (refs/remotes/origin/master)
> git config core.sparsecheckout # timeout=10
> git checkout -f 8e725ea3507f5ac0f8251234e1ff0f214b228d3d
Commit message: "Update docs"
First time build. Skipping changelog.
[Gradle] - Launching build.
[gradle-initializr] $ gradle clean build
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :clean UP-TO-DATE
> Task :compileJava
> Task :compileGroovy NO-SOURCE
> Task :processResources
> Task :classes
> Task :bootJar
> Task :jar SKIPPED
> Task :assemble
> Task :compileTestJava NO-SOURCE
> Task :compileTestGroovy
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :compileIntegrationTestJava NO-SOURCE
> Task :compileIntegrationTestGroovy
> Task :processIntegrationTestResources NO-SOURCE
> Task :integrationTestClasses
> Task :integrationTest
2019-07-17 10:07:48.803 INFO 67741 --- [ Thread-6] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
> Task :check
> Task :build
BUILD SUCCESSFUL in 30s
8 actionable tasks: 7 executed, 1 up-to-date
Build step 'Invoke Gradle script' changed build result to SUCCESS
Finished: SUCCESS
```
As a side note: The GitHub plugin is [currently broken](https://issues.jenkins-ci.org/browse/JENKINS-11337) if you wanted to build multiple branches with a single job. You will have to model it as a multi-branch pipeline job.

View File

@@ -0,0 +1,14 @@
# Exercise 5
In this exercise, you will configure the existing job to execute tests. Furthermore, you'll enhance the job to process the test results and code coverage metrics.
## Displaying JUnit and JaCoCo Test Results
1. Configure the `gradle-initializr` project to parse the JUnit XML results for unit test. The files can be found in the directory `build/test-results/test`.
2. Execute the build twice to generate a graph. Have a look at the executed tests in the test results.
3. Include all test results (unit and integration tests) in the reporting.
4. Have a look at how the test result trend changes.
5. Install the JaCoCo plugin.
6. Reconfigure the Gradle build step to also generate JaCoCo reports: `clean build jacocoTestReport jacocoIntegrationTestReport`.
7. Add a post-build action for publish the JaCoCo reports. Use the following path to look for results: `build/jacoco/**.exec`.
8. Execute the build twice to generate a graph. Have a look at the code coverage results.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,37 @@
# Solution
Publish the JUnit reports by pointing to the exact directory containing the XML files.
![Publish JUnit Results](./images/publish-unit-tests.png)
After executing the build twice you will see a test result trend graph.
![JUnit Test Trend](./images/unit-test-result-trend.png)
Publish the JUnit reporting by using a wild card.
![Publish All JUnit Results](./images/publish-all-tests.png)
The trend changes accordingly.
![Changed Test Result Trend](./images/all-test-result-trend.png)
Install the JaCoCo plugin.
![JaCoCo Plugin](./images/jacoco-plugin.png)
Change the task list executed by the Gradle build step.
![JaCoCo Tasks](./images/jacoco-tasks.png)
Configure JaCoCo reporting.
![JaCoCo Configuration](./images/jacoco-config.png)
The JaCoCo coverage trend renders after executing the build twice.
![JaCoCo Coverage Report Trend](./images/jacoco-trend.png)
You can drill into the details of the report.
![JaCoCo Coverage Report Trend](./images/jacoco-report.png)

View File

@@ -0,0 +1,15 @@
# Exercise 7
You will enhance the existing job to generate a JAR file and store it on Jenkins. The job should record the MD5 hash for the artifact. Later, you will configure a downstream job that should be able to reuse the existing artifact.
## Storing and Fingerprinting Artifacts
1. Create a post-build action for archiving JAR files with the pattern `build/libs/*.jar`. Enable the fingerprinting option.
2. Execute the build. The build should list the artifact `gradle-initializr-1.0.0.jar`.
3. Have a look at the recorded fingerprints of this build.
4. Render the MD5 hash of the artifact and the usage of the artifact.
5. Install the Copy Artifacts plugin.
6. Create a downstream job named `consumer`.
7. Configure the downstream job to use the artifact produced by the upstream job.
8. Run the the build for the job `gradle-initializr`.
9. Have a look at the fingerprints of the downstream job.

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 KiB

View File

@@ -0,0 +1,29 @@
# Solution
In the job configuration, archive the artifact produced by the build. Check the box for generating fingerprints.
![Archive Artifacts](./images/archive-artifacts.png)
Execute the build and find the archived artifact.
![Archived Artifact](./images/build-artifact.png)
Have a look at the details of the fingerprinting.
![Fingerprint Details](./images/fingerprint-details.png)
Install the Copy Artifacts plugin.
![Copy Artifacts Plugin](./images/copy-artifacts-plugin.png)
Trigger the downstream job upon success.
![Trigger Downstream](./images/trigger-upstream.png)
Copy artifacts from the upstream job.
![Copy Artifact From Upstream](./images/copy-artifacts.png)
You can see the full usage of the artifact in upstream and downstream jobs.
![Fingerprint Usage](./images/fingerprints-upstream.png)

View File

@@ -0,0 +1,11 @@
# Exercise 8
In this exercise, you'll create a new user and assign read permissions. You will verify permissions for the admin user and the new user by logging into Jenkins with the appropriate credentials.
## Creating a User and Setting Permissions
1. Ensure that you are logged in as admin user.
2. Create a new user named `miller`. Provide the relevant information.
3. Configure Matrix Security by adding the admin user and the newly created user. The admin user should have all permissions. The `miller` user should only have read permissions for jobs and views.
4. Log out of the system.
5. Log in with the user `miller` and navigate to one of the jobs. Notice that you cannot edit the configuration or trigger a new build.

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB

View File

@@ -0,0 +1,21 @@
# Solution
Create the new user.
![Create User](./images/create-user.png)
The new user is now listed.
![User Overview](./images/user-overview.png)
Configure users and permissions.
![Matrix Security](./images/matrix-security-permissions.png)
Log in with new user.
![Matrix Security](./images/user-login.png)
The job doesn't allow any editing or build triggering operations.
![Job Overview](./images/job-overview.png)

View File

@@ -0,0 +1,17 @@
# Exercise 9
This exercise explores the use of the REST API to trigger a build for an existing job. You will configure security features as necessary.
## Using the REST API for Common Operations
1. Create a new user named `buildbot`.
2. Add adminstration permissions for the user.
3. Log in with user `buildbot`.
4. Generate the API token for the user.
5. Generate a Jenkins crumb from the command line.
6. Export the environment variables `JENKINS_CRUMB` and `JENKINS_API_TOKEN` with the correct values.
7. Trigger a build of the job `gradle-initializr` with a `curl` command using the REST API.
8. Disable the job `gradle-initializr` with a `curl` command using the REST API.
9. Reenable the job `gradle-initializr` with a `curl` command using the REST API.
10. Download the Jenkins CLI client.
11. Trigger the build of the job `gradle-initializr` with a `curl` command using the Jenkins CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,50 @@
# Solution
Create the new user.
![Create User](./images/create-user.png)
Add user permissions.
![User Permissions](./images/user-permissions.png)
Generate the API token.
![API Token](./images/api-token.png)
Generate the Jenkins crumb from the CLI.
```bash
$ curl -u "buildbot:pwd" 'http://localhost:8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)'
Jenkins-Crumb:890d0b4c9c1b111deb55b196813a0ae1
$ export JENKINS_CRUMB=Jenkins-Crumb:890d0b4c9c1b111deb55b196813a0ae1
$ export JENKINS_API_TOKEN=11e2f3c68399b6bc3a28bc06e002be104d
```
Trigger a build with the `curl` command.
```bash
$ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/build
```
Disable the job via the REST API. You will see that the job indicated its status.
```bash
$ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/disable
```
![Disabled Job](./images/disabled-job.png)
Reenable the job.
```bash
$ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/enable
```
Download the Jenkins URL by calling the URL `localhost:8080/jnlpJars/jenkins-cli.jar` from the browser.
In the terminal, navigate to the directory that contains the Jenkins CLI JAR file. Use the Jenkins CLI to trigger a build with the correct command. This simply provide the password instead of the API token.
```bash
$ java -jar jenkins-cli.jar -s http://localhost:8080 -auth buildbot:pwd build gradle-initializr
```

View File

@@ -0,0 +1,35 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
(1..2).each do |number|
config.vm.define "jenkins-agent-#{number}" do |node|
node.vm.network "forwarded_port", guest: 22, host: "909#{number}"
node.vm.network "private_network", ip: "192.168.99.20#{number}"
node.vm.hostname = "jenkins-agent-#{number}"
end
end
config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
end
config.vm.provision "shell", inline: <<-SHELL
sudo add-apt-repository ppa:openjdk-r/ppa -y
sudo apt-get update
sudo apt-get -y install openjdk-8-jdk
sudo update-alternatives --config java
sudo apt-get -y install git
sudo useradd -m jenkins
echo "jenkins:jenkins" | sudo chpasswd
sudo keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts -destkeystore /etc/ssl/certs/java/cacerts.jks -deststoretype JKS -srcstorepass changeit -deststorepass changeit -noprompt
sudo mv /etc/ssl/certs/java/cacerts.jks /etc/ssl/certs/java/cacerts
sudo /var/lib/dpkg/info/ca-certificates-java.postinst configure
SHELL
end

View File

@@ -0,0 +1,17 @@
# Exercise 10
You will start a VM using Vagrant as practice environment to set up a distributed build with 2 agents.
## Configuring and Executing Jobs in a Distributed Build
1. Go to "Manage Jenkins" > "Manage Nodes". You should see a single `master` node.
2. Configure the `master` node by setting the # of executor value to 0. That will take care of never using the `master` for job workload.
3. Have other physical or virtual machines ready that can act as agent nodes. Log into the machine as `root` user and create a new user named `jenkins` with the commands `useradd -d /var/lib/jenkins jenkins` and `passwd jenkins`. From the `master` node, copy the contents of the `id_rsa.key` file to the clipboard. Paste the contents of the clipboard to the file `/var/lib/jenkins/.ssh/authorized_keys`.
4. Add new nodes by clicking "New Nodes". Enter an appropriate name and select the option "Permanent Agent". Use the remote directory `/home/jenkins/jenkins_slave` and set the # of executors to 2. Enter the host and provide credentials by selecting "SSH Username with private key". To keep things easy select "Non verifying Verification Strategy".
4. Trigger a build. You should see that the build is only executed on the agent and not the `master` node.
5. Add at least one more agent.
6. Trigger a build. You should see that the build can be executed on any of the agents.
7. Configure one of the agents to only build jobs with a specific label e.g. `java`. Change the "Usage" field to "Only build jobs with label expressions matching this node".
8. Configure the `gradle-initializr` job and assign the label `java`.
9. Trigger a build of the `gradle-initializr` job. It is only executed by the dedicated agent.
10. Trigger other jobs that do not have the label `java` assigned to them. They are waiting for an agent that can execute the build.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -0,0 +1,29 @@
# Solution
Configure the `master` node.
![Master Configuration](./images/master-config.png)
Add a new agent node.
![Agent Configuration](./images/agent-config.png)
You will see that the `master` node isn't even listed anymore in the executor overview.
![Node Overview](./images/node-overview.png)
Reconfigure the agent node to only build jobs with a specific label.
![Agent Label Configuration](./images/agent-label-config.png)
Reconfigure the job to only use agents that can handle a specific label.
![Job Label Configuration](./images/job-label-config.png)
A build of the job is now only handled by an agent with the assigned label.
![Build For Labeled Agent](./images/build-for-label.png)
Other jobs sit in a queue waiting for an agent that can handle the execution criteria.
![Build For Labeled Agent](./images/queued-job.png)

View File

@@ -0,0 +1,14 @@
# Exercise 11
In this exercise, you will set up a new multi-branch pipeline job and point it to an existing repository on GitHub. You will visualize the pipeline with the standard view and the Blue Ocean plugin.
## Creating a Pipeline Job
1. Have a look at the [repository](https://github.com/bmuschko/todo-spring-boot) `bmuschko/todo-spring-boot` on GitHub. The repository already contains the build definition in the form of a `Jenkinsfile`. Identify each of the steps.
2. In Jenkins, set up the credentials `SONARCLOUD_TOKEN` and `HEROKU_API_KEY` if they don't exist yet.
3. Create a new multi-branch pipeline job for the repository.
4. The initial build is triggered automatically. Have a look at the pipeline in the standard view and its console output.
5. Install the Blue Ocean plugin.
6. Open the Blue Ocean pipeline visualization for the job. Manually trigger the deployment step. Open a browser with the deployed application on Heroku.
7. Create a new branch named `bugfix` and push it to the remote repository. The code should be based off of `master`.
8. Select "Scan Multibranch Pipeline Now". The job should build the new branch.

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 KiB

View File

@@ -0,0 +1,97 @@
# Solution
The created credentials required for the job.
![Credentials](./images/credentials.png)
Create a new item from the dashboard.
![Multi-Branch Pipeline Job](./images/multi-branch-pipeline-job.png)
Point the job to the SCM.
![Multi-Branch Pipeline Job](./images/job-scm.png)
Upon pressing the OK button, the job will scan all available branches in the repository.
```bash
Started
[Thu Jul 18 09:23:53 MDT 2019] Starting branch indexing...
> git --version # timeout=10
> git ls-remote --symref git@github.com:bmuschko/todo-spring-boot.git # timeout=10
Creating git repository in /Users/bmuschko/.jenkins/caches/git-a68a5e08a54549aaef01872e9adb6218
> git init /Users/bmuschko/.jenkins/caches/git-a68a5e08a54549aaef01872e9adb6218 # timeout=10
Setting origin to git@github.com:bmuschko/todo-spring-boot.git
> git config remote.origin.url git@github.com:bmuschko/todo-spring-boot.git # timeout=10
Fetching & pruning origin...
Listing remote references...
> git config --get remote.origin.url # timeout=10
> git --version # timeout=10
> git ls-remote -h git@github.com:bmuschko/todo-spring-boot.git # timeout=10
Fetching upstream changes from origin
> git config --get remote.origin.url # timeout=10
> git fetch --tags --force --progress origin +refs/heads/*:refs/remotes/origin/* --prune
Checking branches...
Checking branch master
Jenkinsfile found
Met criteria
Scheduled build for branch: master
Processed 1 branches
[Thu Jul 18 09:23:57 MDT 2019] Finished branch indexing. Indexing took 4 sec
Finished: SUCCESS
```
You can see the different stages of the pipeline in the standard view.
![Standard Pipeline](./images/standard-pipeline.png)
You can see the different stages of the pipeline in the standard view.
![Standard Pipeline](./images/standard-pipeline.png)
To console output allows for triggering or aborting the manual deployment step.
![Console Manual Step](./images/console-manual-step.png)
Install the Blue Ocean plugin.
![Blue Ocean Plugin](./images/blue-ocean-plugin.png)
The Blue Ocean pipeline view offers a UI element for triggering a manual step.
![Blue Ocean Manual Step](./images/blue-ocean-manual-step.png)
Press the button for processing with the manual step.
![Blue Ocean Triggered Manual Step](./images/triggered-manual-step.png)
The finished pipeline in Blue Ocean.
![Blue Ocean Finished Pipeline](./images/finished-build.png)
Check out the repository and push a new branch.
```bash
$ git clone git@github.com:bmuschko/todo-spring-boot.git
Cloning into 'todo-spring-boot'...
remote: Enumerating objects: 230, done.
remote: Total 230 (delta 0), reused 0 (delta 0), pack-reused 230
Receiving objects: 100% (230/230), 108.69 KiB | 452.00 KiB/s, done.
Resolving deltas: 100% (105/105), done.
$ cd todo-spring-boot
$ git branch bugfix
$ git checkout bugfix
Switched to branch 'bugfix'
$ git push origin bugfix
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'bugfix' on GitHub by visiting:
remote: https://github.com/bmuschko/todo-spring-boot/pull/new/bugfix
remote:
To github.com:bmuschko/todo-spring-boot.git
* [new branch] bugfix -> bugfix
```
After scanning the repository, the new branch will be available and was triggered to build automatically.
![Bugfix Branch](./images/bugfix-branch.png)

View File

@@ -0,0 +1,17 @@
# Exercise 12
In this exercise, you'll create a declarative pipeline for a Go-based project. The `Jenkinsfile` will use some of the basic features of the pipeline DSL.
## Writing a Basic Jenkinsfile
1. Create a new GitHub repository named `go-on-jenkins`.
2. Create a new `Jenkinsfile` in the root directory of the repository as well as a simple `main.go` file. The main function just prints "Hello World!". Initialize the project via Go Modules.
3. Commit the files and push them to the remote repository.
4. Set up a new pipeline job for this repository in Jenkins.
5. Install the [Jenkins Go plugin](https://plugins.jenkins.io/golang).
6. Configure the latest Go runtime as global tool.
7. Enhance the `Jenkinsfile` based on the following requirements. The Jenkinsfile should use the declarative syntax.
* The job can run on all agents.
* The job sets the environment variable `GO111MODULES=on`.
* The job uses the Go runtime from the global tool definition.
* The job specifies one build stage named "Build". The build stage executes the shell command `go build`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 KiB

View File

@@ -0,0 +1,54 @@
# Solution
Create a new job.
![New Job](./images/new-job.png)
Configure the appropriate SCM.
![Job SCM](./images/job-scm.png)
Install the Go plugin.
![Go Plugin](./images/go-plugin.png)
Configure a Go runtime as global tool.
![Go Global Tool](./images/go-global-tool.png)
The `main.go` file could similar to the one below.
```go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
```
The final `Jenkinsfile` looks similar to the solution below.
```groovy
pipeline {
agent any
tools {
go 'go-1.12'
}
environment {
GO111MODULE = 'on'
}
stages {
stage('Build') {
steps {
sh 'go build'
}
}
}
}
```
A build of the job installs the Go runtime and executes the build step.
![Declarative Pipeline](./images/declarative-pipeline.png)

View File

@@ -0,0 +1,20 @@
# Exercise 13
We'll want to enhance the pipeline by additional stages and implement a release workflow. The project is going to use an external tool called [GoReleaser](https://goreleaser.com/) to publish cross-compiled artifacts to [GitHub Releases](https://help.github.com/en/github/administering-a-repository/creating-releases). The binaries should only be released if the commit has been tagged.
## Enhancing a Pipeline With Advanced Features
1. Add stage named `Test` that executes the Go `test` command.
* Add a build step that runs the shell command `go test ./...`.
* Generate code coverage metrics by adding the option `-coverprofile=coverage.txt` to the build step.
* Publish the code coverage metrics to CodeCov by sending a curl command `curl -s https://codecov.io/bash | bash -s -`.
* Log into [CodeCov](https://codecov.io/), determine the CodeCov token for the repository (aka Repository Upload Token) and set it up as credential in Jenkins.
* Retrieve the credential and set the value as environment variable named `CODECOV_TOKEN`.
2. Add a stage named `Code Analysis` that uses [golangci-lint](https://github.com/golangci/golangci-lint) to detect issues with the code.
* Add a build step for installing the `golangci-lint` with the shell command `curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.17.1`
* Add a build step that runs `golangci-lint` with the shell command `golangci-lint run`.
3. Add a stage named `Release` that uses [GoReleaser](https://github.com/goreleaser/goreleaser).
* Only build this step if the commit has been tagged.
* Set up a credential in Jenkins named `github_token` and set your GitHub token.
* Retrieve the credential and set the value as environment variable named `GITHUB_TOKEN`.
* Add a build step that runs the shell command `curl -sL https://git.io/goreleaser | bash`.

Some files were not shown because too many files have changed in this diff Show More