]>
Commit | Line | Data |
---|---|---|
1 | #!/bin/bash | |
2 | ||
3 | # Constants | |
4 | ||
5 | # Proper error codes | |
6 | EXIT_OK=0 | |
7 | EXIT_ERROR=1 | |
8 | EXIT_CONF_ERROR=2 | |
9 | EXIT_NOT_SUPPORTED=3 | |
10 | EXIT_NOT_HANDLED=4 | |
11 | EXIT_COMMAND_NOT_FOUND=127 | |
12 | EXIT_ERROR_ASSERT=128 | |
13 | ||
14 | EXIT_TRUE=0 | |
15 | EXIT_FALSE=1 | |
16 | EXIT_UNKNOWN=2 | |
17 | ||
18 | TRUE=0 | |
19 | FALSE=1 | |
20 | ||
21 | IMAGE_RELEASE=${FALSE} | |
22 | ||
23 | ||
24 | ||
25 | # Functions | |
26 | ||
27 | log() { | |
28 | local level=${1} | |
29 | local message=${2} | |
30 | echo "[${level}] ${message}" | |
31 | } | |
32 | ||
33 | cmd() { | |
34 | local cmd="$@" | |
35 | local ret | |
36 | ||
37 | log DEBUG "Running command: ${cmd}" | |
38 | ||
39 | ${cmd} | |
40 | ret=$? | |
41 | ||
42 | case "${ret}" in | |
43 | ${EXIT_OK}) | |
44 | return ${EXIT_OK} | |
45 | ;; | |
46 | *) | |
47 | log DEBUG "Returned with code '${ret}'" | |
48 | return ${ret} | |
49 | ;; | |
50 | esac | |
51 | } | |
52 | ||
53 | ||
54 | is_mounted() { | |
55 | local mounted_dir=${1} | |
56 | ||
57 | if [ ! -d ${mounted_dir} ]; then | |
58 | log ERROR "Is not a directory ${mounted_dir}" | |
59 | return ${FALSE} | |
60 | else | |
61 | mountpoint ${mounted_dir} | |
62 | fi | |
63 | } | |
64 | ||
65 | unmount_dirs() { | |
66 | local mounted_dir | |
67 | local path | |
68 | ||
69 | local return_value=${EXIT_OK} | |
70 | ||
71 | for mounted_dir in "/proc" "/sys" "/dev/pts" "/dev/shm" "/dev" "/run" "/tmp" "" | |
72 | do | |
73 | path="${IMAGE_MOUNT_DIR}${mounted_dir}" | |
74 | ||
75 | if is_mounted "${path}"; then | |
76 | log DEBUG "Unmounting ${path}" | |
77 | umount "${path}" | |
78 | # Check once again | |
79 | if is_mounted "${path}"; then | |
80 | return_value=${EXIT_ERROR} | |
81 | fi | |
82 | else | |
83 | log DEBUG "${path} is not mounted" | |
84 | fi | |
85 | done | |
86 | ||
87 | } | |
88 | ||
89 | ||
90 | ||
91 | ||
92 | cleanup_stage_1() { | |
93 | ||
94 | if unmount_dirs; then | |
95 | ||
96 | # Remove partition from the kernel table. | |
97 | partx -d ${outlo}p1 | |
98 | ||
99 | # Remove loopback device | |
100 | log DEBUG " Remove loopback device" | |
101 | losetup -d ${outlo} | |
102 | fi | |
103 | ||
104 | ||
105 | # Cleanup Config file for the local repo | |
106 | if [ -f "${LOCAL_REPO_FILE}" ]; then | |
107 | rm -f "${LOCAL_REPO_FILE}" | |
108 | fi | |
109 | ||
110 | } | |
111 | ||
112 | ||
113 | cleanup_stage_2() { | |
114 | if unmount_dirs; then | |
115 | # Drop working dir | |
116 | if [ -d "${WORKING_DIR}" ]; then | |
117 | #rm -dfR "${WORKING_DIR}" | |
118 | echo "" | |
119 | fi | |
120 | fi | |
121 | } | |
122 | ||
123 | cleanup() { | |
124 | cleanup_stage_1 | |
125 | cleanup_stage_2 | |
126 | } | |
127 | ||
128 | generate_image_filename() { | |
129 | local distro=${1} | |
130 | local version=${2} | |
131 | local arch=${3} | |
132 | ||
133 | echo "${distro}-${version}-${arch}" | |
134 | } | |
135 | ||
136 | check_for_pakfire() { | |
137 | local return_value=0 | |
138 | ||
139 | # TODO | |
140 | # Check that pakfire-server binary is available | |
141 | # Check that pakfire binary is available | |
142 | ||
143 | # Check that repo files are installed. (pakfire need to know which repos exist) | |
144 | local repo_dir="/etc/pakfire/repos" | |
145 | ||
146 | if [ ! -d "${repo_dir}" ]; then | |
147 | log ERROR "Could not find respository directory ${repo_dir}" | |
148 | return_value=1 | |
149 | fi | |
150 | ||
151 | return ${return_value} | |
152 | } | |
153 | ||
154 | compress_image() { | |
155 | local compression=${1} | |
156 | local image_file=${2} | |
157 | local level=${3} | |
158 | ||
159 | log debug "Compressing ${image_file} with ${compression}" | |
160 | ||
161 | case "${compression}" in | |
162 | "xz") | |
163 | # Check that the file does not exist yet | |
164 | if [ -f "${image_file}.xz" ]; then | |
165 | log ERROR "Failed to compress the image. The file already exists" | |
166 | return ${EXIT_ERROR} | |
167 | fi | |
168 | cmd xz "-${level}" "${image_file}" | |
169 | ;; | |
170 | "zip") | |
171 | # Check that the file does not exist yet | |
172 | if [ -f "${image_file}.zip" ]; then | |
173 | log ERROR "Failed to compress the image. The file already exists" | |
174 | return ${EXIT_ERROR} | |
175 | ||
176 | fi | |
177 | cmd zip "-${level}" "${image_file}.zip" "${image_file}" | |
178 | # Remove the file which we compressed+ | |
179 | rm -f "${image_file}" | |
180 | ;; | |
181 | ||
182 | esac | |
183 | } | |
184 | ||
185 | reset_root_password() { | |
186 | local root_dir=${1} | |
187 | # Backup passwd file | |
188 | cp -avf ${root_dir}/etc/passwd ${root_dir}/etc/passwd.orig | |
189 | ||
190 | # Drop root password. | |
191 | sed -e "s/^\(root:\)[^:]*:/\1:/" ${root_dir}/etc/passwd.orig > ${root_dir}/etc/passwd | |
192 | ||
193 | # Remove passwd backup file. | |
194 | rm -rvf ${root_dir}/etc/passwd.orig | |
195 | } | |
196 | ||
197 | clone_git_repos() { | |
198 | # Dir where the repos should be located | |
199 | local dir=${1} | |
200 | shift | |
201 | local repos="$@" | |
202 | ||
203 | local repo | |
204 | ||
205 | mkdir -pv ${dir} | |
206 | ||
207 | ( | |
208 | cd ${dir} | |
209 | # Clone git repositories. | |
210 | for repo in ${repos}; do | |
211 | git clone ${repo} | |
212 | done | |
213 | ) | |
214 | } | |
215 | ||
216 | # This function is used to publish the produced images | |
217 | ||
218 | publish() { | |
219 | local path=${1} | |
220 | # The image we created usually a img. file | |
221 | local image_base_file=${2} | |
222 | ||
223 | local image_name_final="$(generate_image_filename "${DISTRO}" "${VERSION}" "${ARCH}")" | |
224 | local image_type | |
225 | ||
226 | # Do these steps for every image format we like to publish | |
227 | for image_type in ${IMAGE_TYPES_PUBLISH}; do | |
228 | # Convert images to the type specified in IMAGE_TYPES_PUBLISH | |
229 | convert_image "${image_type}" "${image_base_file}" "${image_name_final}" | |
230 | ||
231 | # compress image. | |
232 | if [[ ${IMAGE_RELEASE} -eq ${TRUE} ]]; then | |
233 | local compression_type | |
234 | local compression_level | |
235 | ||
236 | ||
237 | # Get compressioon type | |
238 | compression_type="$(get_compression_type ${image_type})" | |
239 | ||
240 | compression_level=1 | |
241 | compress_image "${compression_type}" "${image_name_final}.${image_type}" ${compression_level} | |
242 | ||
243 | # Move images to this path | |
244 | mv -f "${image_name_final}.${image_type}.${compression_type}" ${path} | |
245 | ||
246 | # point the latest links to these images | |
247 | ln -s -f "${path}/${image_name_final}.${image_type}.${compression_type}" \ | |
248 | "${path}/$(generate_image_filename "${DISTRO}" "latest" "${ARCH}").${image_type}.${compression_type}" | |
249 | else | |
250 | ||
251 | # Move images to this path | |
252 | mv -f "${image_name_final}.${image_type}" ${path} | |
253 | fi | |
254 | done | |
255 | ||
256 | } | |
257 | ||
258 | convert_image() { | |
259 | local type=${1} | |
260 | local from=${2} | |
261 | local to=${3} | |
262 | ||
263 | if [[ ${type} = "img" ]]; then | |
264 | # We do not need to convert the image here but we need to rename | |
265 | mv -f ${from} ${to}.${type} | |
266 | return $? | |
267 | fi | |
268 | ||
269 | if [[ ${type} = "qcow2" ]]; then | |
270 | local command="qemu-img convert -c -O ${type} ${from} ${to}.${type}" | |
271 | else | |
272 | local command="qemu-img convert -O ${type} ${from} ${to}.${type}" | |
273 | fi | |
274 | ||
275 | cmd ${command} | |
276 | } | |
277 | ||
278 | get_compression_type() { | |
279 | local image_type=${1} | |
280 | ||
281 | case "${image_type}" in | |
282 | "qcow2" | "img") | |
283 | # These types can be used only under Unix so we use xz as compression | |
284 | echo "xz" | |
285 | ;; | |
286 | "vmdk" | "vdi") | |
287 | # These types can be also under Windows so we use zip as compression | |
288 | echo "zip" | |
289 | ;; | |
290 | esac | |
291 | ||
292 | } | |
293 | ||
294 | check_for_free_space() { | |
295 | local space=${1} | |
296 | local path=${2} | |
297 | local space_in_path=0 | |
298 | ||
299 | space_in_path=$(df -h -B MB --output=avail ${path} | tail -n 1) | |
300 | space_in_path=${space_in_path%MB} | |
301 | log debug ${space_in_path} | |
302 | log debug ${space} | |
303 | ||
304 | if [ ${space_in_path} -lt ${space} ]; then | |
305 | log error "Not enough free space available under ${path}" | |
306 | log error "Free space is ${space_in_path}MB but we need at least ${space}MB" | |
307 | return ${EXIT_ERROR} | |
308 | fi | |
309 | } | |
310 | ||
311 | parse_cmdline() { | |
312 | while [ $# -gt 0 ]; do | |
313 | case "${1}" in | |
314 | "--release") | |
315 | IMAGE_RELEASE=${TRUE} | |
316 | ;; | |
317 | ||
318 | *) | |
319 | error "Invalid argument: ${1}" | |
320 | return ${EXIT_CONF_ERROR} | |
321 | ;; | |
322 | esac | |
323 | shift | |
324 | done | |
325 | } | |
326 | ||
327 | chroot_wrapper() { | |
328 | local chroot_dir="${1}" | |
329 | ||
330 | shift | |
331 | ||
332 | local command | |
333 | ||
334 | if [ ! -d ${chroot_dir} ]; then | |
335 | log ERROR "Cannot chroot in a non directory ${chroot_dir}" | |
336 | fi | |
337 | ||
338 | mount proc "${chroot_dir}/proc" -t proc -o nosuid,noexec,nodev | |
339 | mount sys "${chroot_dir}/sys" -t sysfs -o nosuid,noexec,nodev,ro | |
340 | mount udev "${chroot_dir}/dev" -t devtmpfs -o mode=0755,nosuid | |
341 | mount devpts "${chroot_dir}/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec | |
342 | mount shm "${chroot_dir}/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev | |
343 | mount /run "${chroot_dir}/run" --bind | |
344 | mount tmp "${chroot_dir}/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid | |
345 | ||
346 | for command in "$@" | |
347 | do | |
348 | cmd chroot "${chroot_dir}" "${command}" | |
349 | done | |
350 | ||
351 | umount "${chroot_dir}/proc" | |
352 | umount "${chroot_dir}/sys" | |
353 | umount "${chroot_dir}/dev/pts" | |
354 | umount "${chroot_dir}/dev/shm" | |
355 | umount "${chroot_dir}/dev" | |
356 | umount "${chroot_dir}/run" | |
357 | umount "${chroot_dir}/tmp" | |
358 | } |