7 for (( index
=${#CLEANUP[@]}-1 ; index
>= 0 ; index--
)) ;do
16 REASON
="${2:-Unknown reason}"
22 trap cleanup EXIT TERM INT
29 ARCH
# The image architecture
30 IMAGE_TYPE
# The image type to create
31 VARIANT
# The variant of the base image to use
32 PROFILE
# The ansible group to apply to the new image
33 GIT_BRANCH
# The git branch of the automation repo to checkout
34 GIT_URL
# The git URL of the automation repo to checkout
35 LXD_CLIENT_CERT
# Path to LXD client certificate
36 LXD_CLIENT_KEY
# Path to LXD client certificate key
37 SSH_PRIVATE_KEY
# Path to SSH private key
38 TEST
# 'true' to test launching published image
41 for var
in "${REQUIRED_VARIABLES[@]}" ; do
42 if [ ! -v "$var" ] ; then
44 echo "Missing required variable: '${var}'" >&2
47 if [[ ! "${MISSING_VARS}" == "0" ]] ; then
48 fail
1 "Missing required variables"
51 # Default optional variables
52 INSTANCE_START_TIMEOUT
="${INSTANCE_START_TIMEOUT:-120}"
53 NETWORK_SLEEP
="${NETWORK_SLEEP:-15}"
56 apt-get
-y install lxd-client ansible jq
59 mkdir
-p ~
/.config
/lxc
60 cp "${LXD_CLIENT_CERT}" ~
/.config
/lxc
/client.crt
61 cp "${LXD_CLIENT_KEY}" ~
/.config
/lxc
/client.key
63 "rm -f ${HOME}/.config/lxc/client.crt"
64 "rm -f ${HOME}/.config/lxc/client.key"
66 lxc remote add ci
--accept-certificate --auth-type tls
"${LXD_HOST}"
70 git clone
-b "${GIT_BRANCH}" "${GIT_URL}" ci
71 cd ci
/automation
/ansible ||
exit 1
73 SOURCE_IMAGE_NAME
="${OS}/${RELEASE}/${VARIANT}/${ARCH}"
74 # Include IMAGE_TYPE since an alias may only be defined once even if the
75 # type of the image differs
76 TARGET_IMAGE_NAME
="${OS}/${RELEASE}/${VARIANT}/${ARCH}/${PROFILE}/${IMAGE_TYPE}"
78 # Try from local cache
80 if [ "${IMAGE_TYPE}" == "vm" ] ; then
86 # It's possible that concurrent image creation when running parallel jobs causes
87 # an error during the launch:
88 # Error: Failed instance creation: UNIQUE constraint failed: images.project_id, images.fingerprint
89 # C.f. https://github.com/canonical/lxd/issues/11636
93 while [[ "${TRIES}" -lt "${TRIES_MAX}" ]] ; do
94 if ! INSTANCE_NAME
=$
(lxc
-q launch
"${VM_ARG[@]}" -p default -p "${LXD_INSTANCE_PROFILE}" "${SOURCE_IMAGE_NAME}/${IMAGE_TYPE}") ; then
96 if ! INSTANCE_NAME
=$
(lxc
-q launch
"${VM_ARG[@]}" -p default -p "${LXD_INSTANCE_PROFILE}" images:"${SOURCE_IMAGE_NAME}") ; then
98 echo "Failed to deployed ephemereal instance attempt ${TRIES}/${TRIES_MAX}"
99 if [[ "${TRIES}" -lt "${TRIES_MAX}" ]] ; then
102 fail
1 "Failed to deploy ephemereal instance"
110 INSTANCE_NAME
="$(echo "${INSTANCE_NAME}" | cut -d ':' -f 2 | tr -d ' ')"
114 "lxc delete -f ${INSTANCE_NAME}"
115 "lxc stop ${INSTANCE_NAME}"
118 # VMs may take more time to start, wait until instance is running
119 TIME_REMAINING
="${INSTANCE_START_TIMEOUT}"
122 INSTANCE_STATUS
=$
(lxc
exec "${INSTANCE_NAME}" hostname
)
124 if [[ "${INSTANCE_STATUS}" == "${INSTANCE_NAME}" ]] ; then
128 TIME_REMAINING
=$
((TIME_REMAINING
- 1))
129 if [ "${TIME_REMAINING}" -lt "0" ] ; then
130 fail
1 "Timed out waiting for instance to become available via 'lxc exec'"
134 # Wait for cloud-init to finish
135 if [[ "${VARIANT}" == "cloud" ]] ; then
136 # It's possible for cloud-init to fail, but to still be able to continue.
137 # Eg., a profile asks for netplan.io on a system that doesn't have that
139 lxc
exec "${INSTANCE_NAME}" -- cloud-init status
-w || true
142 # Wait for instance to have an ip address (@TODO: is there a better approach?)
143 sleep "${NETWORK_SLEEP}"
145 # @TODO: Handle case when iputils2 is not installed
147 POTENTIAL_INTERFACES
=(eth0 enp5s0
)
148 lxc
exec "${INSTANCE_NAME}" -- ip a
150 for interface
in "${POTENTIAL_INTERFACES[@]}" ; do
151 if ! DEV_INFO
="$(lxc exec "${INSTANCE_NAME}" -- ip a show dev "${interface}")" ; then
154 INSTANCE_IP
="$(echo "${DEV_INFO}" | grep -Eo 'inet [^ ]* ' | cut -d' ' -f2 | cut -d'/' -f1)"
155 if [[ "${INSTANCE_IP}" != "" ]] ; then
160 if [[ "${INSTANCE_IP}" == "" ]] ; then
161 fail
1 "Failed to determine instance IP address"
164 ssh-keyscan
"${INSTANCE_IP}" >> ~
/.ssh
/known_hosts2
165 #lxc exec "${INSTANCE_NAME}" -- bash -c 'for i in /etc/ssh/ssh_host_*_key ; do ssh-keygen -l -f "$i" ; done' >> "${HOME}/.ssh/known_hosts"
167 "rm -f ${HOME}/.ssh/known_hosts2"
169 cp "${SSH_PRIVATE_KEY}" ~
/.ssh
/id_rsa
170 ssh-keygen
-f ~
/.ssh
/id_rsa
-y > ~
/.ssh
/id_rsa.pub
172 "rm -f ${HOME}/.ssh/id_rsa.pub"
173 "rm -f ${HOME}/.ssh/id_rsa"
175 lxc
file push ~
/.ssh
/id_rsa.pub
"ci:${INSTANCE_NAME}/root/.ssh/authorized_keys2"
176 # Some distros, eg. Rocky Linux, don't enable the use of authorized_keys2
178 lxc
exec "ci:${INSTANCE_NAME}" -- bash
-c 'if test -f /etc/redhat-release ; then sed -i "s#^AuthorizedKeysFile.*#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2#" /etc/ssh/sshd_config ; systemctl restart sshd ; fi'
181 # Confirm working SSH connection
182 if ! ssh "${INSTANCE_IP}" hostname
; then
183 fail
1 "Unable to reach ephemereal instance over SSH"
187 cat > fake-inventory
<<EOF
192 "rm -f $(pwd)/fake-inventory"
195 LANG
=C ANSIBLE_STRATEGY
=linear ansible-playbook site.yml \
196 -e '{"compilers_legacy_install": false, "jenkins_user": false, "lttng_modules_checkout_repo": false}' \
197 -l "${INSTANCE_IP}" -i fake-inventory
199 # Cleanup instance side
200 LANG
=C ANSIBLE_STRATEGY
=linear ansible-playbook \
201 playbooks
/post-imagebuild-clean.yml \
202 -l "${INSTANCE_IP}" -i fake-inventory
205 lxc stop
"${INSTANCE_NAME}"
208 if FINGERPRINT
=$
(lxc publish
"${INSTANCE_NAME}" 2>&1 |
grep -E -o '[A-Fa-f0-9]{64}') ; then
209 echo "Published instance with fingerprint '${FINGERPRINT}'"
211 fail
1 "No fingerprint for published instance"
216 if [[ "${TEST}" == "true" ]] ; then
218 while [[ "${TRIES}" -lt "${TRIES_MAX}" ]] ; do
219 if ! INSTANCE_NAME
=$
(lxc
-q launch
-e "${VM_ARG[@]}" -p default -p "${LXD_INSTANCE_PROFILE}" "${FINGERPRINT}") ; then
221 echo "Failed to launch instance try ${TRIES}/${TRIES_MAX}"
222 if [[ "${TRIES}" -lt "${TRIES_MAX}" ]] ; then
223 sleep $
((1 + RANDOM
% 10))
226 fail
1 "Failed to launch an instance using newly published image '${FINGERPRINT}'"
228 INSTANCE_NAME
="$(echo "${INSTANCE_NAME}" | cut -d':' -f2 | tr -d ' ')"
230 "lxc stop -f ${INSTANCE_NAME}"
238 lxc image
alias delete
"${TARGET_IMAGE_NAME}" || true
239 lxc image
alias create
"${TARGET_IMAGE_NAME}" "${FINGERPRINT}"