~8 minute read
This post explains how to configure Jenkins for the FreeBSD Hardware CI Lab. The work done here is based on earlier work using an Intel NUC and Rasberry PI [1][2] along with work pioneered by the main Continuous Integration server, https://ci.freebsd.org/.[3][4][5][6]
lang/python3devel/jenkinsdevel/py-jenkins-job-builderdevel/gitsecurity/sudolang/groovy(optional)- May be useful for creating/testing configurations used by Jenkins.
lang/expect(optional)- Can be useful for testing expect scripts (or autogenerating scripts using autoexpect) but requires some TCL knowledge.
Setup rc.conf to enable Jenkins and then start the Jenkins server.
sudo sysrc local_unbound_enable=YES
sudo sysrc jenkins_enable=YES
sudo sysrc jenkins_home=/usr/local/jenkins
sudo sysrc jenkins_args='--webroot=${jenkins_home}/war --httpPort=8180'
sudo service jenkins startThen navigate to localhost:8180 or <hostname>:8180 where <hostname> is the hostname of the machine running Jenkins. This will be referred to as <jenkins_url> from here on.
You should then be prompted to input the initial admin password. Choose "Select plugins to install" and then click "None" at the top to deselect all plugins and continue (you can install all necessary plugins later). Continue to follow the prompts to create the initial admin account (in my case the user is called admin) and setup Jenkins.
Plugins can be installed at <jenkins_url>/pluginManager/available. And install the following plugins:
- Git
- Subversion
- Build Timeout
- Clang Scan-Build
- Timestamper
- Parameterized Trigger
- EnvInject
- Warnings Next Generation
- Copy Artifact Plugin
- PostBuildScript
- Conditional Buildstep
- Matrix-Based Security (optional - matrix based authentication for users)
- Embeddable Build Status (optional - get status buttons)
- Email Extension (optional - mailing after builds)
- Green Balls (optional - green for success)
- Blue Ocean (optional - prettier interface)
Create folders for Jenkins to work in.
sudo mkdir -p /jenkins/jails
sudo mkdir -p /jenkins/workspace
chown jenkins:jenkins /jenkins /jenkins/*Create a sudoers file for jenkins /usr/local/etc/sudoers.d/jenkins. This will allow jenkins to run the commands it needs to run in superuser without a password.
Cmnd_Alias CI_COMMANDS = /usr/sbin/jail, /usr/sbin/jexec, /sbin/mount, /sbin/umount, /sbin/devfs, /bin/chflags, /bin/rm, /usr/sbin/pkg, /usr/bin/tar, /sbin/ifconfig, /usr/bin/tee, /bin/mkdir, /sbin/mdconfig
Defaults:jenkins !env_reset
jenkins ALL=(root) NOPASSWD: CI_COMMANDS
Clone the the HW CI repository into an easily accessible location. You may want to fork this repository before checking it out.
git clone https://github.com/GeraldNDA/freebsd-ciNOTE
This repository contains configurations and build scripts used on each build. By default, scripts are cloned from the repository any build that requires it. As a result, if you make any changes don't forget to push them (
git push) after your commits.
default.yamldefines default definitions for all jobsmacro.yamldefines various custom build and post-build tasksdevice-template.yamldefines templates for building FreeBSD for devices and testing themdevice-test.yamldefines the branches/target architectures/devices that should be built and tested. This is where the majority of your changes will go.jail.yamldefines macros related to working in a jailmail-notify.yamldefines macros related to getting email notificationsscm.yamldefines macros related to SCMs. This is where support for new repositories and branches should be added.
Job configurations are in the jjb directory and managed by Jenkins Job Builder (JJB).
In order to modify any configurations, you should first make a jenkins_jobs.ini file by copying the sample. You'll want to change the username to match the username used. As well you can create an API Token for yourself at <jenkins_url>/user/<username>/configure and use that as JJB's configured password.
WARNING
While you could use your admin password instead of the API Token, it is not suggested to use a plaintext password with JJB for security reasons.
If you have forked the repository, you'll want to modify default.yaml and change ci-scripts-git-path to match the new repo.
NOTE
If the 'master' node is being used, you can also use a local path. This is done by modifying
device-template.yamland uncommenting the linescheckout-scripts-localand commenting out thecheckout-scripts.
To update Jenkins with the configurations, run the following:
jenkins-jobs --conf jenkins_jobs.ini update --delete-old .Navigate to <jenkins_url>/configureSecurity/ and under the "Agents" section, enable "TCP port for JNLP agents". If you don't have a firewall, "Random" is a good option.
This will enable you to launch nodes via Java Web Start
Go to <jenkins_url>/computer/new
Ensure the following configurations are set:
Namecan be anything you wantLabelsshould includejailerif this is to be where jails are createdUsageshould beuse this node as much as possibleLaunch methodshould belaunch agent via Java Web StartEnvironment Variablesshould have the following values:-
BUILDER_0_IP4should be some IP address in the same subnet as the IP address used to access the internet.WARNING
If it isn't defined correctly,
pkgwill fail when trying to build FreeBSD.NOTE
Also define
BUILDER_n_IP4for everynth additional executor. UseBUILDER_n_IP6if IP6 IP addresses are used instead. -
BUILDER_JFLAGis the number of cores build jobs should use. Usesyctl kern.smp.cpusto choose this number. -
BUILDER_MEMORYis the amount of memory build jobs should use (e.g.8G). This should be based on the amount of memory the node/computer has. -
BUILDER_NETIFis the network interface the build jails should use. This should match the network interface to the internet (e.g.em0) -
BUILDER_ZFS_PARENTis the parent directory of jails. This should be/jenkins/jails. The build scripts do not use ZFS.NOTE
If you are using ZFS you may want to use the jail scripts for: https://github.com/freebsd/freebsd-ci/blob/master/scripts/jail instead of the included ones.
-
The following setup is tested on the master machine (that does not use ZFS). If you are using a distributed machine that does use ZFS you may benefit from looking at the steps used in https://wiki.freebsd.org/Jenkins/Setup. These steps should also be applicable on a distributed machine (with Jenkins installed).
First, enable jails:
sudo sysrc jail_enable=YES
sudo service jail startThe following tasks should be run as the jenkins user:
sudo su jenkins
Load the Jenkins scripts in the /jenkins directory
cd /jenkins
git clone https://github.com/GeraldNDA/jenkins-agent-scripts
cd jenkins-agent-scriptsCopy agent.conf.sample into agent.conf. You'll need to update jenkins_url, agentname and secret values to match the value what's given at <jenkins_url>/computer/${agentname}/. At this url, you should see "Run from agent command line" which gives a command like this: java -jar agent.jar -jnlpUrl ${jenkins_url}/computer/${agentname}/agent-agent.jnlp -secret ${secret}.
Configure the crontab to keep the instance running:
crontab crontabTo start the agent, run
daemon -cf keepalive.shYou should see at <jenkins_url>/computer/${hostname}/ that the node is running successfully.
Given the following jobs in FreeBSD-device-tests (in device-test.yaml)
- 'FreeBSD-{branch}-aarch64-device_tests':
disable_build: true
target: arm64
target_arch: aarch64
warnscanner: clang
device_name:
- pinea64
# Each 32-bit arm device has it's own CONF file so they have their own build job.
- 'FreeBSD-{branch}-beaglebone-device_tests':
target: arm
target_arch: armv7
warnscanner: clang
device_name: beaglebone
conf_name: BEAGLEBONELet's say you wanted to add another 32-bit device like the jetsontk1 and another aarch64 device like the rockpine64 and two amd devices (desktop1 and performancepc1) (Assuming they are already configured for net booting). After doing so your configuration file would like so:
- 'FreeBSD-{branch}-aarch64-device_tests':
disable_build: true
target: arm64
target_arch: aarch64
warnscanner: clang
device_name:
- pinea64
- rockpine64
- 'FreeBSD-{branch}-amd-device_tests':
disable_build: true
target: amd
target_arch: amd
warnscanner: clang
device_name:
- desktop1
- performancepc1
- 'FreeBSD-{branch}-beaglebone-device_tests':
target: arm
target_arch: armv7
warnscanner: clang
device_name: beaglebone
conf_name: BEAGLEBONE
- 'FreeBSD-{branch}-jetsontk1-device_tests':
target: arm
target_arch: armv7
warnscanner: clang
device_name: beaglebone
conf_name: JETSON-TK1Additionally, add the following build groups for each job to add device/architecture specific builds as necessary.
- job-group:
name: 'FreeBSD-{branch}-amd-device_tests'
jobs:
- 'FreeBSD-{branch}-{target_arch}-build_devtest'
- 'FreeBSD-device-{branch}-{device_name}-test'
- job-group:
name: 'FreeBSD-{branch}-jetsontk1-device_tests'
jobs:
- 'FreeBSD-{branch}-{device_name}-build_devtest'
- 'FreeBSD-device-{branch}-{device_name}-testAlso, add these devices to the conserver.cf file so that they can be accessed by the power control and serial scripts. A template conserver.cf file is described here.
Additionally, you'll have to add jobs for each of these. At the time of writing the repository includes a sample device specific (beaglebone) and architecture specific (pinea64) build job and test job. Thus it should be sufficient to just copy them. And then follow the steps for modifying tests to ensure they will work. For example:
cp -r jobs/FreeBSD-device-head-beaglebone-test jobs/FreeBSD-device-head-jetsontk1-test
cp -r jobs/FreeBSD-device-head-beaglebone-build_devtest jobs/FreeBSD-device-head-jetsontk1-build_devtest
cp -r jobs/FreeBSD-device-head-pinea64-test jobs/FreeBSD-device-head-desktop1-test
cp -r jobs/FreeBSD-device-head-pinea64-test jobs/FreeBSD-device-head-performancepc1-test
cp -r jobs/FreeBSD-head-aarch64-build_devtest jobs/FreeBSD-device-head-amd-build_devtestFor architecture-specific builds, the defaults should work as expected. However, for device-specific builds you will need to update some files.
Each build job contains:
job/<jobname>/build.sh- the script that runs the buildscripts/build/build-world-kernel-head.sh- the script which performs the build and creates the artifacts used for installing the build (and NFS booting the devices)job/<jobname>/jail.conf- File which contains configurations for jail environmentjob/<jobname>/src.conf- File which contains additional build configurations. For device-specific builds, it should containKERNCONF=<DEVICE>-NFS.GENERIC-NFSused for architecture specific builds. Contains a Kernel configuration based onGENERICbut adds NFS support<DEVICE>-NFSused for device-specific builds. Contains a kernel configuration based on<DEVICE>(or whichever kernel config is correct for this device).
For device tests, the default tests simply install the files and turn on the device. They expect FreeBSD to boot successfully and will then power off the device.
Each test job depends on the following files:
job/<jobname>/build.sh- the script that runs the testsscripts/test/extract-artifacts.sh- the script that extracts the built files and installs them in a directoryscripts/test/run-device-tests.sh- runs the device scripts and loads any overrides to these scriptsscripts/test/device_tests/test_device.py- a boilerplate test script that powers on the device and verifies that FreeBSD boots successfully. Will also source any additional tests if specified.job/<jobname>/device_tests/tests.py- the sourced python file for specifying additional tests
Example job/<jobname>/device_tests/tests.py:
# Either (or both) of these lists should be defined.
# The lists should be iterables of functions as specified below.
to_run = []
panic_actions = []
class TestError(Exception):
pass
def check_status(ch, msg):
res = ch.expect(["0", r"\d+"])
if res == 1:
raise TestFailure(f"Failed to do task '{msg}'")
ch.console.expect(ch.CMDLINE_RE)
def example_test(ch):
"""
`ch` is the console handler
- before running this function, the device has already been booted and logged into.
`ch.timeout` is the boot timeout
- default value is 5 minutes, otherwise every expect timeout is 1 minute.
`ch.console` is the Pexpect instance for running commands
- see Pexpect.spawn documentation for more info
`ch.device` is the device instance.
`ch.device.name` is the device name
`ch.device.turn_on/turn_off` are methods for controlling the power to the device.
`ch.CMDLINE_RE` is the regex that matches the command line PS1
"""
try:
ch.console.sendline("ls /")
ch.console.expect(ch.CMDLINE_RE)
check_status(ch, "Look for files at root")
ch.console.sendline("dmesg")
ch.console.expect(ch.CMDLINE_RE)
check_status(ch, "Check boot messages")
except TestFailure as e:
return (False, str(e))
# Can return (status, message) or nothing.
# return (True, "All's good!")
def bt_on_panic(ch):
# before running this function, the device panicked during boot
ch.console.sendline("bt")
# You can add multiple functions to either list. The will be run in the order of the list.
to_run.append(example_test)
panic_actions.append(bt_on_panic)You can specify additional tests to run after logging in or after a panic occurs (to extract debug information).
NOTE
Patches to this issue have been submitted and it should be resolved soon.
In the <jenkins_home> directory (i.e. /usr/local/jenkins) remove any JNA files. You can use the following to do so.
sudo find war -name "*jna*" -exec sudo rm {} +
sudo find plugins -name "*jna*" -exec sudo rm {} + Also delete any lingering workspaces (these may contain lock files which will prevent further checkouts)
sudo rm -rf /jenkins/workspace/*And then restart jenkins
sudo service jenkins restartChecking out from SVN should work properly now.
BUILDER_0_IP4 must be on the same subnet as the ip address used to access the interface. You can use a tool like this: http://meridianoutpost.com/resources/etools/network/two-ips-on-same-network.php to verify that your IP is on the same subnet i.e. if em0 uses ip address inet 192.168.32.164 netmask 0xffffff00 broadcast 192.168.32.255 then any address from 192.168.32.1 to 192.168.32.255 would be on the same subnet. Avoid using values at the highest range, because those are usually used as the "broadcast address" and avoid values too low as they are usually used as the "router address".
[1]: https://ci-dev.freebsd.org/job/rpi3-test/
[2]: https://github.com/lwhsu/testbed
[3]: https://wiki.freebsd.org/Jenkins
[4]: https://wiki.freebsd.org/Jenkins/Setup