Merge ~ubuntu-docker-images/ubuntu-docker-images/+git/utils:software-versions-image-extraction into ~ubuntu-docker-images/ubuntu-docker-images/+git/utils:master

Proposed by Bryce Harrington
Status: Merged
Approved by: Bryce Harrington
Approved revision: ffa06060cc9ea82b9af7ae74306a2c19c0120443
Merge reported by: Bryce Harrington
Merged at revision: ffa06060cc9ea82b9af7ae74306a2c19c0120443
Proposed branch: ~ubuntu-docker-images/ubuntu-docker-images/+git/utils:software-versions-image-extraction
Merge into: ~ubuntu-docker-images/ubuntu-docker-images/+git/utils:master
Diff against target: 258 lines (+124/-46)
2 files modified
README.software-versions.md (+20/-0)
software-versions.sh (+104/-46)
Reviewer Review Type Date Requested Status
Athos Ribeiro Approve
Canonical Server Pending
Sergio Durigan Junior Pending
Review via email: mp+401343@code.launchpad.net

Description of the change

This implements the remaining functionality for looking up version info from the docker images. The text table output is getting a bit dense, but here's how it looks now:

Software focal hirsute
postgres: 12.6-0ubuntu0.20.04.1 12.6-0ubuntu0.20.04.1 (12.6) 13.2-1 ??? (13.2)
memcached: 1.5.22-2 1.5.22-2ubuntu0.1 (1.5.22) 1.6.9+dfsg-1 1.6.9+dfsg-1 (1.6.9)
apache2: 2.4.41-4ubuntu3.1 2.4.41-4ubuntu3.1 (2.4.41) 2.4.46-4ubuntu1 ??? (2.4.46)
mysql: 8.0.23-0ubuntu0.20.04.1 8.0.23-0ubuntu0.20.04.1 (8.0.23) 8.0.23-3ubuntu1 8.0.23-3build1 (8.0.23)
redis: 5:5.0.7-2 5:5.0.7-2 (5.0.7) 5:6.0.11-1 5:6.0.11-1 (6.0.11)
nginx: 1.18.0-0ubuntu1 1.18.0-0ubuntu1 (1.18.0) 1.18.0-6ubuntu8 1.18.0-6ubuntu4 (1.18.0)

(Note it's already telling us there are update opportunities for focal's memcached and hirsute's nginx.)

And check out the JSON report:

  https://paste.ubuntu.com/p/qmDxnRhKcM/

You'll note a huge amount of duplicate code between the text and json reports. I haven't decided whether to refactor that better, or just drop the text table -- as soon as we add another Ubuntu release to the mix the width of this table will become too unwieldy -- but whichever way, I'll leave that to a future MP.

You'll also note I'm still using hard-coded lists of images rather than using list-all-images.sh since I need to have some associated data. Refactoring this is also planned but left for a future MP.

To post a comment you must log in.
Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

Nice work! LGTM

I have only one question: should we either remove the pulled images or add an option to do so? If this script is running constantly in some CI environment, the internal docker storage may grow considerably depending on the frequency that new images get released.

In other words, would it be worth to sacrifice network data consumption in a CI environment (always pulling images even when no new images were pushed to the registries) to possibly avoid storage issues in the future?

Alternatively, we could keep only the latest versions (the one holding the tag) of each image in the host running the script.

review: Approve
Revision history for this message
Bryce Harrington (bryce) wrote :

Thanks for the review!

That's a good thought about cleanup, I hadn't thought that far ahead but you're right. For what I plan to use this for, it would be helpful to retain previously pulled images in a cache, however over time this would for sure build up. That maybe could be dealt with by a weekly or monthly purge and refresh, but perhaps there are better solutions out there. I've added a trello card for myself to track this:

  https://trello.com/c/dYw9NgO2/3933-software-versions-cache-control-of-pulled-images

Revision history for this message
Bryce Harrington (bryce) wrote :

I've landed this change to utils master branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/README.software-versions.md b/README.software-versions.md
2new file mode 100644
3index 0000000..1eec933
4--- /dev/null
5+++ b/README.software-versions.md
6@@ -0,0 +1,20 @@
7+Software Versions
8+=================
9+
10+software-versions.sh is used to review what versions of software
11+packages provided in the Ubuntu OCI images, returning results either as
12+JSON data or as a formatted text table.
13+
14+Currently, this only displays OCI images that ship debian-based
15+software.
16+
17+
18+Requirements
19+------------
20+
21+You have to have the following programs installed in your system in
22+order to run software-versions.sh:
23+
24+- distro-info
25+- docker
26+- rmadison
27diff --git a/software-versions.sh b/software-versions.sh
28index 1bb3125..b10fbdb 100755
29--- a/software-versions.sh
30+++ b/software-versions.sh
31@@ -93,7 +93,7 @@ upstream_version() {
32 ## @param package_name: Name of the source or binary package to look up.
33 ## @param distro_codename: Ubuntu release name to check.
34 ## @param type: Lookup either "source" or "binary" packages.
35-package_version() {
36+package_version_in_archive() {
37 local package_name="${1}"
38 local distro_codename="${2}"
39 local type="${3:-source}"
40@@ -107,25 +107,57 @@ package_version() {
41 suites="${distro_codename},${distro_codename}-updates,${distro_codename}-security"
42 fi
43
44- local source_or_binary
45 if [ "${type}" = "source" ]; then
46- source_or_binary="-a \"source\""
47- fi
48-
49- rmadison \
50- "${source_or_binary}" \
51- -s "${suites}" \
52- "${package_name}" \
53- | cut -d\| -f2 \
54+ rmadison -a "source" -s "${suites}" "${package_name}"
55+ else
56+ rmadison -s "${suites}" "${package_name}"
57+ fi | cut -d\| -f2 \
58 | sort --version-sort \
59 | tail -n 1 \
60 | xargs
61 }
62
63-source_package() {
64- local template v M N
65+## Look up version of binary package in a Docker OCI image.
66+##
67+## @param image_id: Image to query from.
68+## @param package_name: Name of the binary package to look up.
69+package_version_in_image() {
70+ local image_id binary_package_name
71+ image_id="${1}"
72+ binary_package_name="${2}"
73+
74+ [ -n "${image_id}" ] || return 1
75+ [ -n "${binary_package_name}" ] || return 1
76+
77+ version=$(docker run --rm \
78+ --name "${binary_package_name}-version-check" \
79+ "${image_id}" \
80+ apt-cache policy "${binary_package_name}" \
81+ | grep "Installed:" | cut -d: -f2- | xargs) \
82+ || ( warn "Could not get ${image_id} from docker"; return 1 )
83+
84+ echo "${version}"
85+}
86+
87+
88+#########################
89+### Software Versions ###
90+#########################
91+
92+## Expand a package name template with the software version number.
93+##
94+## The template syntax uses {M}, {N}, and {M.N} as the variable
95+## placeholders for the version number.
96+##
97+## @param template: String template to be filled in.
98+## @param v: The 'M.N' version number to substitute.
99+substitute_version() {
100+ declare -a v
101+ local template M N
102 template="${1}"
103- v=( "${2//./ }" )
104+
105+ # shellcheck disable=SC2206
106+ v=( ${2//./ } )
107 M="${v[0]}"
108 N="${v[1]}"
109
110@@ -135,11 +167,6 @@ source_package() {
111 sed -e "s/{M.N}/${M}.${N}/g"
112 }
113
114-
115-#########################
116-### Software Versions ###
117-#########################
118-
119 ## Returns the M.N version from a package version.
120 ##
121 ## We define "software version" to mean the initial two values in the
122@@ -150,16 +177,23 @@ source_package() {
123 ## @param str version: The package version to extract M.N from.
124 ## @stdout: The M.N version string.
125 software_version() {
126- local version v version_major version_minor
127+ declare -a v
128+ local version version_major version_minor
129 version=$(upstream_version "${1}")
130
131- v=( "${version//./ }" )
132+ # shellcheck disable=SC2206
133+ v=( ${version//./ } )
134 version_major="${v[0]}"
135 version_minor="${v[1]}"
136
137- echo "${version_major}.${version_minor}"
138+ if [ -z "${version_minor}" ]; then
139+ echo "${version_major}"
140+ else
141+ echo "${version_major}.${version_minor}"
142+ fi
143 }
144
145+
146 ############
147 ### Main ###
148 ############
149@@ -175,13 +209,13 @@ main() {
150
151 # Debian packages in focal
152 declare -A SOFTWARE=(
153- # Software Source Binary
154- [apache2]=" apache{M} apache2"
155- [memcached]=" memcached memcached"
156- [mysql]=" mysql-{M.N} mysql-server"
157- [nginx]=" nginx nginx"
158- [postgres]=" postgresql-{M} postgresql"
159- [redis]=" redis redis-server"
160+ # Software Source Metapackage Installed-Binary
161+ [apache2]=" apache2 apache2 apache2"
162+ [memcached]=" memcached memcached memcached"
163+ [mysql]=" mysql-{M.N} mysql-server mysql-server-core-{M.N}"
164+ [nginx]=" nginx nginx nginx-common"
165+ [postgres]=" postgresql-{M} postgresql postgresql-{M}"
166+ [redis]=" redis redis-server redis-server"
167 )
168
169 declare -ar CODENAMES=(
170@@ -197,20 +231,30 @@ main() {
171 printf "%s " "${CODENAMES[-1]}"
172 printf "\n"
173 for software_name in "${!SOFTWARE[@]}"; do
174- local RECORD=() source_package_template binary_package_name
175+ local RECORD=() source_package_template
176+ local meta_package_name binary_package_name
177
178 source_package_template=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $1}')
179- binary_package_name=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $2}')
180+ meta_package_name=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $2}')
181+ binary_package_template=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $3}')
182
183 local codename
184 for codename in "${CODENAMES[@]}"; do
185- local binary_package_version software_version source_package version version_upstream
186- binary_package_version=$(package_version "${binary_package_name}" "${codename}" "binary" )
187- software_version=$(software_version "${binary_package_version}")
188- source_package=$(source_package "${source_package_template}" "${software_version}")
189- version=$(package_version "${source_package}" "${codename}" "source")
190+ local meta_package_version mn source_package_name image_id
191+ local version version_in_image version_upstream distro_release
192+ meta_package_version=$(package_version_in_archive "${meta_package_name}" "${codename}" "binary" )
193+ mn=$(software_version "${meta_package_version}")
194+ source_package_name=$(substitute_version "${source_package_template}" "${mn}")
195+ binary_package_name=$(substitute_version "${binary_package_template}" "${mn}")
196+ version=$(package_version_in_archive "${source_package_name}" "${codename}" "source")
197 version_upstream=$(upstream_version "${version}")
198- RECORD+=("${version} (${version_upstream})")
199+
200+ distro_release=$(distro-info --series "${codename}" --release | cut -d' ' -f1)
201+ image_id="ubuntu/${software_name}:${mn}-${distro_release}_edge"
202+ version_in_image="???"
203+ docker image pull "${image_id}" 2>/dev/null >/dev/null \
204+ && version_in_image=$(package_version_in_image "${image_id}" "${binary_package_name}")
205+ RECORD+=("${version} <${version_in_image}> (${version_upstream})")
206 done
207
208 printf "%-25s " "${software_name}:"
209@@ -224,25 +268,39 @@ main() {
210 echo "["
211 local num_software=${#SOFTWARE[@]}
212 for software_name in "${!SOFTWARE[@]}"; do
213- local RECORD=() source_package_template binary_package_name
214- source_package_template=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $1}')
215- binary_package_name=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $2}')
216+ local RECORD=() source_package_template
217+ local meta_package_name binary_package_name
218+
219 echo " {"
220 echo " \"software_name\": \"${software_name}\","
221
222+ source_package_template=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $1}')
223+ meta_package_name=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $2}')
224+ binary_package_template=$(echo "${SOFTWARE[${software_name}]}" | awk '{print $3}')
225+
226 local codename
227 local num_codenames=${#CODENAMES[@]}
228 for codename in "${CODENAMES[@]}"; do
229- local binary_package_version software_version source_package version version_upstream
230- binary_package_version=$(package_version "${binary_package_name}" "${codename}" "binary" )
231- software_version=$(software_version "${binary_package_version}")
232- source_package=$(source_package "${source_package_template}" "${software_version}")
233- version=$(package_version "${source_package}" "${codename}" "source")
234+ local meta_package_version mn source_package_name image_id
235+ local version version_in_image version_upstream distro_release
236+ meta_package_version=$(package_version_in_archive "${meta_package_name}" "${codename}" "binary" )
237+ mn=$(software_version "${meta_package_version}")
238+ source_package_name=$(substitute_version "${source_package_template}" "${mn}")
239+ binary_package_name=$(substitute_version "${binary_package_template}" "${mn}")
240+ version=$(package_version_in_archive "${source_package_name}" "${codename}" "source")
241 version_upstream=$(upstream_version "${version}")
242+
243+ distro_release=$(distro-info --series "${codename}" --release | cut -d' ' -f1)
244+ image_id="ubuntu/${software_name}:${mn}-${distro_release}_edge"
245+ version_in_image="???"
246+ docker image pull "${image_id}" 2>/dev/null >/dev/null \
247+ && version_in_image=$(package_version_in_image "${image_id}" "${binary_package_name}")
248+
249 echo " \"${codename}\": {
250- \"source_package\": \"${source_package}\",
251- \"software_version\": \"${software_version}\",
252+ \"source_package\": \"${source_package_name}\",
253+ \"software_version\": \"${mn}\",
254 \"version\": \"${version}\",
255+ \"version_in_image\": \"${version_in_image}\",
256 \"version_upstream\": \"${version_upstream}\""
257 if [ "${num_codenames}" = "1" ]; then
258 echo " }"

Subscribers

People subscribed via source and target branches

to all changes: