-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·249 lines (219 loc) · 8.9 KB
/
build.sh
File metadata and controls
executable file
·249 lines (219 loc) · 8.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/bin/bash
# This script is used to build the OpenShift Docker images.
#
# OS - Specifies distribution - "rhel8", "rhel9", "rhel10", "rhel11", "c9s", "c10s" or "fedora"
# VERSION - Specifies the image version - (must match with subdirectory in repo)
# SINGLE_VERSION - Specifies the image version - (must match with subdirectory in repo)
# VERSIONS - Must be set to a list with possible versions (subdirectories)
set -E
[ -n "${DEBUG:-}" ] && set -x
# shellcheck shell=bash
# shellcheck source=/dev/null
source "$(dirname "${BASH_SOURCE[0]}")"/common.sh
OS=${1-$OS}
VERSION=${2-$VERSION}
error() { echo "ERROR: $*" ; false ; }
trap 'echo "errexit on line $LINENO, $0" >&2' ERR
MAX_BUILD_ATTEMPTS=2
# "best-effort" cleanup of image
function clean_image {
for id_file in .image-id .image-id-from; do
if test -f $id_file; then
local id
id=$(cat $id_file)
test -n "$id" || continue
docker rmi --force "$id" || :
rm -f "$id_file" || :
fi
done
}
# Pull image based on FROM, before we build our own.
function pull_image {
local dockerfile="$1"
local loops=10
local loop=0
# Get image_name from Dockerfile before pulling.
while read -r line; do
if ! grep -q "^FROM" <<< "$line"; then
continue
fi
image_name=$(echo "$line" | cut -d ' ' -f2)
# In case FROM scratch is defined, skip it
if [[ "$image_name" == "scratch" ]]; then
continue
fi
echo "-> Pulling image $image_name before building image from $dockerfile."
# Sometimes in Fedora case it fails with HTTP 50X
# Check if the image is available locally and try to pull it if it is not
if [[ "$(docker images -q "$image_name" 2>/dev/null)" != "" ]]; then
echo "The image $image_name is already pulled."
continue
fi
# Try pulling the image to see if it is accessible
# WORKAROUND: Since Fedora registry sometimes fails randomly, let's try it more times
while ! docker pull "$image_name" > .image-id-from; do
((loop++)) || :
echo "Pulling image $image_name failed."
[ "$loop" -gt "$loops" ] && { echo "It happened $loops times. Giving up." ; return 1; }
echo "Let's wait $((loop*5)) seconds and try again."
sleep "$((loop*5))"
done
done < "$dockerfile"
}
# Perform docker build but append the LABEL with GIT commit id at the end
function docker_build_with_version {
local dockerfile="$1"
local exclude=.exclude-${OS}
local devel_repo_file=.devel-repo-${OS}
local devel_repo_var="DEVEL_REPO_$OS"
local build_args_file=.build-args-${OS}
local is_podman
local ret_code
if [ -e "$exclude" ]; then
echo "-> $exclude file exists for version $dir, skipping build."
clean_image
return
fi
if [ ! -e "$dockerfile" ]; then
echo "-> $dockerfile for version $dir does not exist, skipping build."
clean_image
return
fi
echo "-> Version ${dir}: building image from '${dockerfile}' ..."
# We need to check '.git' dir in root directory
if [ -d "../.git" ] ; then
git_version=$(git rev-parse --short HEAD)
BUILD_OPTIONS+=" --label io.openshift.builder-version=\"${git_version}\""
fi
# Add possibility to use a development repo
#
# This is useful if we want to work with RPMs that are not available publically yet.
#
# How to use it:
# First, we create a file that only tells the scripts to use the development repository,
# e.g. .devel-repo-rhel8, similarly as we use .exclude-rhel8 for excluding particular
# variant of the Dockerfile. Content of the file is not important at this point.
#
# If such a file exists in the repository, then the building scripts will take a look
# at a correspondent variable, e.g. DEVEL_REPO_rhel8, and will use the repository file
# defined by that variable.
#
# That means that definition of the DEVEL_REPO_rhel8 variable is a responsibility of
# the test/CI environment.
if [ -f "$devel_repo_file" ] && [[ -v "$devel_repo_var" ]] ; then
CUSTOM_REPO=$(mktemp)
curl -Lk "${!devel_repo_var}" >"${CUSTOM_REPO}"
echo "-> $devel_repo_file file exists for version $dir, so using ${!devel_repo_var}."
fi
if [ -n "$CUSTOM_REPO" ]; then
if [ -f "$CUSTOM_REPO" ]; then
BUILD_OPTIONS+=" -v $CUSTOM_REPO:/etc/yum.repos.d/sclorg_custom.repo:Z"
elif [ -d "$CUSTOM_REPO" ]; then
BUILD_OPTIONS+=" -v $CUSTOM_REPO:/etc/yum.repos.d/:Z"
else
echo "ERROR: file type not known: $CUSTOM_REPO" >&2
fi
fi
# If a specific Dockerfile requires some specific build options (like capabilities),
# let them be included in the build command.
# The content of the .build-opts-<OS> will be used as part of the docker build command.
if [ -f "$build_args_file" ] ; then
build_opts_from_file="$(cat "$build_args_file")"
echo '⚠️ Be aware, using additional build arguments: ' "${build_opts_from_file}"
BUILD_OPTIONS+=" ${build_opts_from_file}"
fi
pull_image "$dockerfile"
docker info 2>/dev/null | grep podman 1>/dev/null && is_podman=1 || is_podman=0
# squash is possible only for podman. In docker it is usable only in experimental mode.
if [[ "$SKIP_SQUASH" -eq 0 ]] && [[ "$is_podman" -eq 1 ]]; then
BUILD_OPTIONS+=" --squash"
fi
i=1
build_failed=1
while [ $i -le $MAX_BUILD_ATTEMPTS ]; do
command="docker build ${BUILD_OPTIONS} -f $dockerfile ${DOCKER_BUILD_CONTEXT}"
echo "-> building using $command"
set +x -o pipefail
tmp_file=$(mktemp "/tmp/${dir}-${OS}.XXXXXX")
$command 2>&1 | tee "$tmp_file"
ret_code=$?
set -x +o pipefail
echo "Return code from docker build is '$ret_code'."
last_row=$(< "$tmp_file" tail -n 1)
if [[ $ret_code != "0" ]]; then
# In case of failure, we want to analyze the logs and send them to pastebin or logdetective.
# The failure can be ethel issue like network failure or registry failure.
# Red Hat Enterprise Linux 9 for x86_64 - AppStre 0.0 B/s | 0 B 00:00
# Errors during downloading metadata for repository 'rhel-9-for-x86_64-appstream-rpms':
# - Curl error (56): Failure when receiving data from the peer for https:// [Received HTTP code 503 from proxy after CONNECT]
# Error: Failed to download metadata for repo 'rhel-9-for-x86_64-appstream-rpms':
if [[ "${OS}" == "rhel8" ]] || [[ "${OS}" == "rhel9" ]] || [[ "${OS}" == "rhel10" ]] || [[ "${OS}" == "rhel11" ]]; then
# Do not fail in case of sending log to pastebin or logdetective fails.
analyze_logs_by_logdetective "${tmp_file}"
fi
((i++))
sleep 5
echo "Retrying to build image for version $dir, attempt $i of $MAX_BUILD_ATTEMPTS."
else
# Structure of log build is as follows:
# COMMIT
# --> e191d12b5928
# e191d12b5928360dd6024fe80d31e08f994d42577f76b9b143e014749afc8ab4
# shellcheck disable=SC2016
if [[ "$last_row" =~ (^-->)?(Using cache )?[a-fA-F0-9]+$ ]]; then
IMAGE_ID="$last_row"
fi
echo "$IMAGE_ID" > .image-id
tag_image
build_failed=0
break
fi
rm -f "$tmp_file"
done
if [[ $build_failed -ne 0 ]]; then
echo "-> Build failed for version $dir and OS $OS after 2 attempts, giving up."
exit 1
fi
}
function tag_image {
name=$(docker inspect -f "{{.Config.Labels.name}}" "$IMAGE_ID") || (echo "-> No image with this tag found, try re-building." && return)
# We need to check '.git' dir in root directory
if [ -d "../.git" ] ; then
commit_date=$(git show -s HEAD --format=%cd --date=short | sed 's/-//g')
date_and_hash="${commit_date}-$(git rev-parse --short HEAD)"
else
date_and_hash="$(date +%Y%m%d%H%M%S)"
fi
full_reg_name="$REGISTRY$name"
echo "-> Tagging image '$IMAGE_ID' as '$full_reg_name:$dir' and '$full_reg_name:latest' and '$full_reg_name:$OS' and '$full_reg_name:$date_and_hash'"
docker tag "$IMAGE_ID" "$full_reg_name:$OS"
docker tag "$IMAGE_ID" "$full_reg_name:$dir"
docker tag "$IMAGE_ID" "$full_reg_name:latest"
docker tag "$IMAGE_ID" "$full_reg_name:$date_and_hash"
}
# Versions are stored in subdirectories. You can specify VERSION variable
# to build just one single version. By default we build all versions
dirs=${VERSION:-$VERSIONS}
if [ -n "${SINGLE_VERSION:-}" ]; then
if [ "$SINGLE_VERSION" != "$VERSION" ]; then
echo "Skipping build for $VERSION. SINGLE_VERSION is defined $SINGLE_VERSION and does not equal to $VERSION."
exit 0
fi
dirs=$SINGLE_VERSION
if [[ "${SINGLE_VERSION}" == *"minimal"* ]]; then
echo "Adding ${SINGLE_VERSION//-minimal/} because it might be needed for testing $SINGLE_VERSION."
dirs="$dirs ${SINGLE_VERSION//-minimal/}"
fi
if [[ "${SINGLE_VERSION}" == *"micro"* ]]; then
echo "Adding ${SINGLE_VERSION//-micro/} because it might be needed for testing $SINGLE_VERSION."
dirs="$dirs ${SINGLE_VERSION//-micro/}"
fi
fi
echo "Built versions are: $dirs"
for dir in ${dirs}; do
# shellcheck disable=SC2164
pushd "${dir}" > /dev/null
docker_build_with_version Dockerfile."$OS"
# shellcheck disable=SC2164
popd > /dev/null
done