Merge ~lamoura/ubuntu/+source/ubuntu-advantage-tools:upload-27.8-jammy into ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel

Proposed by Lucas Albuquerque Medeiros de Moura
Status: Merged
Merged at revision: 06882843231c960ea65fdd1fcb2d9f06fec8b071
Proposed branch: ~lamoura/ubuntu/+source/ubuntu-advantage-tools:upload-27.8-jammy
Merge into: ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel
Diff against target: 3242 lines (+1504/-374)
41 files modified
debian/changelog (+21/-0)
features/_version.feature (+1/-1)
features/attach_validtoken.feature (+6/-6)
features/attached_commands.feature (+23/-18)
features/attached_enable.feature (+103/-17)
features/aws-ids.yaml (+1/-0)
features/azure-ids.yaml (+1/-0)
features/enable_fips_vm.feature (+48/-37)
features/realtime_kernel.feature (+128/-0)
features/staging_commands.feature (+1/-1)
features/ubuntu_pro.feature (+9/-9)
features/ubuntu_pro_fips.feature (+270/-9)
features/ubuntu_upgrade.feature (+1/-2)
features/unattached_status.feature (+85/-68)
help_data.yaml (+10/-0)
lib/upgrade_lts_contract.py (+25/-5)
tools/refresh-keyrings.sh (+4/-1)
tox.ini (+3/-0)
uaclient/clouds/identity.py (+2/-0)
uaclient/clouds/tests/test_identity.py (+6/-3)
uaclient/config.py (+1/-1)
uaclient/contract.py (+1/-1)
uaclient/entitlements/__init__.py (+2/-0)
uaclient/entitlements/base.py (+19/-9)
uaclient/entitlements/cis.py (+1/-1)
uaclient/entitlements/fips.py (+69/-44)
uaclient/entitlements/livepatch.py (+5/-0)
uaclient/entitlements/realtime.py (+84/-0)
uaclient/entitlements/tests/test_cc.py (+2/-0)
uaclient/entitlements/tests/test_fips.py (+60/-35)
uaclient/entitlements/tests/test_livepatch.py (+8/-0)
uaclient/entitlements/tests/test_repo.py (+17/-7)
uaclient/messages.py (+46/-0)
uaclient/status.py (+4/-4)
uaclient/tests/test_cli_attach.py (+2/-2)
uaclient/tests/test_cli_collect_logs.py (+2/-2)
uaclient/tests/test_cli_status.py (+49/-39)
uaclient/tests/test_upgrade_lts_contract.py (+67/-1)
uaclient/tests/test_util.py (+221/-27)
uaclient/util.py (+95/-23)
uaclient/version.py (+1/-1)
Reviewer Review Type Date Requested Status
Robie Basak ubuntu-sru Approve
Canonical Server Pending
Review via email: mp+419498@code.launchpad.net

Description of the change

This MR represents the release of 27.8 for Jammy.
We have already uploaded the new package into our staging PPA:
https://launchpad.net/~ua-client/+archive/ubuntu/staging/

And we have already created an SRU bug for it:
https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bug/1969125

Let us know if there is anything else we need to address here

To post a comment you must log in.
Revision history for this message
Robie Basak (racb) wrote :

+1 from an SRU perspective. I would like the usual QA done on this though. For a 0-day SRU, I'm not sure how this would work and have asked in #ubuntu-release. The upload can go in now though as it'll be held in Unapproved since it's seeded.

review: Approve (ubuntu-sru)
Revision history for this message
Sergio Durigan Junior (sergiodj) :
Revision history for this message
Chad Smith (chad.smith) wrote :

LGTM. Versioning looks good as previous upload to -proposed was rejected.

Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Uploaded:

$ dput ubuntu-advantage-tools_27.8~22.04.1_source.changes
Trying to upload package to ubuntu
Checking signature on .changes
gpg: /home/sergio/work/ubuntu-advantage-tools/ubuntu-advantage-tools_27.8~22.04.1_source.changes: Valid signature from 106DA1C8C3CBBF14
Checking signature on .dsc
gpg: /home/sergio/work/ubuntu-advantage-tools/ubuntu-advantage-tools_27.8~22.04.1.dsc: Valid signature from 106DA1C8C3CBBF14
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading ubuntu-advantage-tools_27.8~22.04.1.dsc: done.
  Uploading ubuntu-advantage-tools_27.8~22.04.1.tar.xz: done.
  Uploading ubuntu-advantage-tools_27.8~22.04.1_source.buildinfo: done.
  Uploading ubuntu-advantage-tools_27.8~22.04.1_source.changes: done.
Successfully uploaded packages.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index b44d4d9..a272000 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,24 @@
6+ubuntu-advantage-tools (27.8~22.04.1) jammy; urgency=medium
7+
8+ * New upstream release 27.8 (LP: #1969125)
9+ - entitlements: apply overrides from the contract response
10+ - fips:
11+ + unhold fips packages when enabling fips-updates
12+ + Automatically disable fips service before enabling fips-updates
13+ + unhold more packages when enabling fips
14+ - lib: fix upgrade script for unsupported releases (LP: #1968067)
15+ - realtime: add support for realtime kernel beta service on Jammy
16+
17+ -- Lucas Moura <lucas.moura@canonical.com> Wed, 13 Apr 2022 18:17:02 -0300
18+
19+ubuntu-advantage-tools (27.7.1~22.04.1) jammy; urgency=medium
20+
21+ * fips:
22+ - make fips service incompatible with fips-updates
23+ - unhold more packages when enabling fips
24+
25+ -- Lucas Moura <lucas.moura@canonical.com> Thu, 31 Mar 2022 16:18:36 -0300
26+
27 ubuntu-advantage-tools (27.7~22.04.1) jammy; urgency=medium
28
29 * d/changelog:
30diff --git a/features/_version.feature b/features/_version.feature
31index b972500..35b73f7 100644
32--- a/features/_version.feature
33+++ b/features/_version.feature
34@@ -15,7 +15,7 @@ Feature: UA is expected version
35 Scenario Outline: Check ua version
36 Given a `<release>` machine with ubuntu-advantage-tools installed
37 When I run `dpkg-query --showformat='${Version}' --show ubuntu-advantage-tools` with sudo
38- Then stdout matches regexp:
39+ Then I will see the following on stdout
40 """
41 {UACLIENT_BEHAVE_CHECK_VERSION}
42 """
43diff --git a/features/attach_validtoken.feature b/features/attach_validtoken.feature
44index 25f763b..35fd90f 100644
45--- a/features/attach_validtoken.feature
46+++ b/features/attach_validtoken.feature
47@@ -11,7 +11,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
48 And I run `ua status --all` as non-root
49 Then stdout matches regexp:
50 """
51- SERVICE ENTITLED STATUS DESCRIPTION
52+ SERVICE +ENTITLED STATUS DESCRIPTION
53 cc-eal +yes +n/a +Common Criteria EAL2 Provisioning Packages
54 cis +yes +n/a +Security compliance and audit tools
55 esm-apps +yes +n/a +UA Apps: Extended Security Maintenance \(ESM\)
56@@ -58,7 +58,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
57 """
58 And stdout matches regexp:
59 """
60- SERVICE ENTITLED STATUS DESCRIPTION
61+ SERVICE +ENTITLED STATUS DESCRIPTION
62 cc-eal +yes +<cc_status> +Common Criteria EAL2 Provisioning Packages
63 """
64 And stdout matches regexp:
65@@ -315,7 +315,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
66 """
67 And stdout matches regexp:
68 """
69- SERVICE ENTITLED STATUS DESCRIPTION
70+ SERVICE +ENTITLED STATUS DESCRIPTION
71 cc-eal +yes +<cc_status> +Common Criteria EAL2 Provisioning Packages
72 """
73 And stdout matches regexp:
74@@ -375,7 +375,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
75 """
76 And stdout matches regexp:
77 """
78- SERVICE ENTITLED STATUS DESCRIPTION
79+ SERVICE +ENTITLED STATUS DESCRIPTION
80 cc-eal +yes +<cc_status> +Common Criteria EAL2 Provisioning Packages
81 """
82 And stdout matches regexp:
83@@ -435,7 +435,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
84 """
85 And stdout matches regexp:
86 """
87- SERVICE ENTITLED STATUS DESCRIPTION
88+ SERVICE +ENTITLED STATUS DESCRIPTION
89 cc-eal +yes +<cc_status> +Common Criteria EAL2 Provisioning Packages
90 """
91 And stdout matches regexp:
92@@ -477,7 +477,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
93 When I run `ua status` with sudo
94 Then stdout matches regexp:
95 """
96- SERVICE ENTITLED STATUS DESCRIPTION
97+ SERVICE +ENTITLED STATUS DESCRIPTION
98 cc-eal +yes +<cc-eal> +Common Criteria EAL2 Provisioning Packages
99 """
100 And stdout matches regexp:
101diff --git a/features/attached_commands.feature b/features/attached_commands.feature
102index dd6cf1a..f3ed252 100644
103--- a/features/attached_commands.feature
104+++ b/features/attached_commands.feature
105@@ -129,9 +129,9 @@ Feature: Command behaviour when attached to an UA subscription
106
107 Examples: ubuntu release
108 | release | valid_services |
109- | xenial | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates. |
110- | bionic | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates. |
111- | focal | cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates, usg. |
112+ | xenial | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch,\nrealtime-kernel, ros, ros-updates. |
113+ | bionic | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch,\nrealtime-kernel, ros, ros-updates. |
114+ | focal | cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, realtime-kernel,\nros, ros-updates, usg. |
115
116 @series.xenial
117 @series.bionic
118@@ -148,8 +148,8 @@ Feature: Command behaviour when attached to an UA subscription
119 And stderr matches regexp:
120 """
121 Cannot disable unknown service 'foobar'.
122- Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,
123- ros-updates.
124+ Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch,
125+ realtime-kernel, ros, ros-updates.
126 """
127 And I verify that running `ua disable esm-infra` `as non-root` exits `1`
128 And stderr matches regexp:
129@@ -187,8 +187,8 @@ Feature: Command behaviour when attached to an UA subscription
130 And stderr matches regexp:
131 """
132 Cannot disable unknown service 'foobar'.
133- Try cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,
134- ros-updates, usg.
135+ Try cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, realtime-kernel,
136+ ros, ros-updates, usg.
137 """
138 And I verify that running `ua disable esm-infra` `as non-root` exits `1`
139 And stderr matches regexp:
140@@ -231,7 +231,7 @@ Feature: Command behaviour when attached to an UA subscription
141 When I run `ua status --all` as non-root
142 Then stdout matches regexp:
143 """
144- SERVICE AVAILABLE DESCRIPTION
145+ SERVICE +AVAILABLE DESCRIPTION
146 cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
147 """
148 Then stdout matches regexp:
149@@ -241,6 +241,7 @@ Feature: Command behaviour when attached to an UA subscription
150 fips +<fips> +NIST-certified core packages
151 fips-updates +<fips> +NIST-certified core packages with priority security updates
152 livepatch +yes +Canonical Livepatch service
153+ realtime-kernel +<realtime-kernel> +Beta-version Ubuntu Kernel with PREEMPT_RT patches
154 ros +<ros> +Security Updates for the Robot Operating System
155 ros-updates +<ros> +All Updates for the Robot Operating System
156 """
157@@ -280,10 +281,10 @@ Feature: Command behaviour when attached to an UA subscription
158 """
159
160 Examples: ubuntu release
161- | release | esm-apps | cc-eal | cis | fips | fips-update | ros | cis_or_usg |
162- | xenial | yes | yes | yes | yes | yes | yes | cis |
163- | bionic | yes | yes | yes | yes | yes | yes | cis |
164- | focal | yes | no | yes | yes | yes | no | usg |
165+ | release | esm-apps | cc-eal | cis | fips | fips-update | ros | cis_or_usg | realtime-kernel |
166+ | xenial | yes | yes | yes | yes | yes | yes | cis | no |
167+ | bionic | yes | yes | yes | yes | yes | yes | cis | no |
168+ | focal | yes | no | yes | yes | yes | no | usg | no |
169
170 @series.all
171 @uses.config.machine_type.lxd.container
172@@ -361,8 +362,8 @@ Feature: Command behaviour when attached to an UA subscription
173 And I run `ua status --all` with sudo
174 Then stdout matches regexp:
175 """
176- SERVICE ENTITLED STATUS DESCRIPTION
177- cc-eal no
178+ SERVICE +ENTITLED STATUS DESCRIPTION
179+ cc-eal +no
180 """
181 When I run `ua --version` as non-root
182 Then I will see the uaclient version on stdout with features ` +disable_auto_attach +machine_token_overlay -other`
183@@ -403,8 +404,8 @@ Feature: Command behaviour when attached to an UA subscription
184 And stderr matches regexp:
185 """
186 Cannot disable unknown service 'foobar'.
187- Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,
188- ros-updates.
189+ Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch,
190+ realtime-kernel, ros, ros-updates.
191 """
192 When I run `ua status` with sudo
193 Then stdout matches regexp:
194@@ -449,8 +450,8 @@ Feature: Command behaviour when attached to an UA subscription
195 And stderr matches regexp:
196 """
197 Cannot disable unknown service 'foobar'.
198- Try cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,
199- ros-updates, usg.
200+ Try cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, realtime-kernel,
201+ ros, ros-updates, usg.
202 """
203 When I run `ua status` with sudo
204 Then stdout matches regexp:
205@@ -558,6 +559,8 @@ Feature: Command behaviour when attached to an UA subscription
206 \(https://ubuntu.com/security/certifications#fips\)
207 - livepatch: Canonical Livepatch service
208 \(https://ubuntu.com/security/livepatch\)
209+ - realtime-kernel: Beta-version Ubuntu Kernel with PREEMPT_RT patches
210+ \(https://ubuntu.com/realtime-kernel\)
211 - ros-updates: All Updates for the Robot Operating System
212 \(https://ubuntu.com/robotics/ros-esm\)
213 - ros: Security Updates for the Robot Operating System
214@@ -657,6 +660,8 @@ Feature: Command behaviour when attached to an UA subscription
215 \(https://ubuntu.com/security/certifications#fips\)
216 - livepatch: Canonical Livepatch service
217 \(https://ubuntu.com/security/livepatch\)
218+ - realtime-kernel: Beta-version Ubuntu Kernel with PREEMPT_RT patches
219+ \(https://ubuntu.com/realtime-kernel\)
220 - ros-updates: All Updates for the Robot Operating System
221 \(https://ubuntu.com/robotics/ros-esm\)
222 - ros: Security Updates for the Robot Operating System
223diff --git a/features/attached_enable.feature b/features/attached_enable.feature
224index 90a62cb..7944100 100644
225--- a/features/attached_enable.feature
226+++ b/features/attached_enable.feature
227@@ -669,7 +669,7 @@ Feature: Enable command behaviour when attached to an UA subscription
228 And stdout is a json matching the `ua_operation` schema
229 And I will see the following on stdout:
230 """
231- {"_schema_version": "0.1", "errors": [{"message": "Cannot enable FIPS when Livepatch is enabled.", "service": "fips", "type": "service"}], "failed_services": ["fips"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
232+ {"_schema_version": "0.1", "errors": [{"message": "Cannot enable FIPS when Livepatch is enabled.", "message_code": "incompatible-service-stops-enable", "service": "fips", "type": "service"}], "failed_services": ["fips"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
233 """
234
235 @slow
236@@ -693,6 +693,7 @@ Feature: Enable command behaviour when attached to an UA subscription
237 Then I will see the following on stdout
238 """
239 One moment, checking your subscription first
240+ Disabling incompatible service: Livepatch
241 Updating package lists
242 Installing FIPS packages
243 FIPS enabled
244@@ -761,21 +762,21 @@ Feature: Enable command behaviour when attached to an UA subscription
245 And I run `ua status --all` as non-root
246 Then stdout matches regexp
247 """
248- ros yes disabled Security Updates for the Robot Operating System
249+ ros +yes disabled Security Updates for the Robot Operating System
250 """
251 When I run `ua enable ros --assume-yes --beta` with sudo
252 And I run `ua status --all` as non-root
253 Then stdout matches regexp
254 """
255- ros yes enabled Security Updates for the Robot Operating System
256+ ros +yes enabled Security Updates for the Robot Operating System
257 """
258 And stdout matches regexp
259 """
260- esm-apps yes enabled UA Apps: Extended Security Maintenance \(ESM\)
261+ esm-apps +yes enabled UA Apps: Extended Security Maintenance \(ESM\)
262 """
263 And stdout matches regexp
264 """
265- esm-infra yes enabled UA Infra: Extended Security Maintenance \(ESM\)
266+ esm-infra +yes enabled UA Infra: Extended Security Maintenance \(ESM\)
267 """
268 When I verify that running `ua disable esm-apps` `with sudo` and stdin `N` exits `1`
269 Then stdout matches regexp
270@@ -793,11 +794,11 @@ Feature: Enable command behaviour when attached to an UA subscription
271 When I run `ua status --all` as non-root
272 Then stdout matches regexp
273 """
274- ros yes disabled Security Updates for the Robot Operating System
275+ ros +yes disabled Security Updates for the Robot Operating System
276 """
277 And stdout matches regexp
278 """
279- esm-apps yes disabled UA Apps: Extended Security Maintenance \(ESM\)
280+ esm-apps +yes disabled UA Apps: Extended Security Maintenance \(ESM\)
281 """
282 When I verify that running `ua enable ros --beta` `with sudo` and stdin `N` exits `1`
283 Then stdout matches regexp
284@@ -818,15 +819,15 @@ Feature: Enable command behaviour when attached to an UA subscription
285 When I run `ua status --all` as non-root
286 Then stdout matches regexp
287 """
288- ros yes enabled Security Updates for the Robot Operating System
289+ ros +yes enabled Security Updates for the Robot Operating System
290 """
291 And stdout matches regexp
292 """
293- esm-apps yes enabled UA Apps: Extended Security Maintenance \(ESM\)
294+ esm-apps +yes enabled UA Apps: Extended Security Maintenance \(ESM\)
295 """
296 And stdout matches regexp
297 """
298- esm-infra yes enabled UA Infra: Extended Security Maintenance \(ESM\)
299+ esm-infra +yes enabled UA Infra: Extended Security Maintenance \(ESM\)
300 """
301 When I run `apt-cache policy` as non-root
302 Then apt-cache policy for the following url has permission `500`
303@@ -840,7 +841,7 @@ Feature: Enable command behaviour when attached to an UA subscription
304 And I run `ua status --all` as non-root
305 Then stdout matches regexp
306 """
307- ros-updates yes enabled All Updates for the Robot Operating System
308+ ros-updates +yes enabled All Updates for the Robot Operating System
309 """
310 When I run `apt-cache policy` as non-root
311 Then apt-cache policy for the following url has permission `500`
312@@ -869,11 +870,11 @@ Feature: Enable command behaviour when attached to an UA subscription
313 When I run `ua status --all` as non-root
314 Then stdout matches regexp
315 """
316- ros-updates yes enabled All Updates for the Robot Operating System
317+ ros-updates +yes enabled All Updates for the Robot Operating System
318 """
319 And stdout matches regexp
320 """
321- ros yes enabled Security Updates for the Robot Operating System
322+ ros +yes enabled Security Updates for the Robot Operating System
323 """
324 When I run `ua disable ros-updates --assume-yes` with sudo
325 When I run `ua disable ros --assume-yes` with sudo
326@@ -883,19 +884,19 @@ Feature: Enable command behaviour when attached to an UA subscription
327 When I run `ua status --all` as non-root
328 Then stdout matches regexp
329 """
330- ros-updates yes enabled All Updates for the Robot Operating System
331+ ros-updates +yes enabled All Updates for the Robot Operating System
332 """
333 And stdout matches regexp
334 """
335- ros yes enabled Security Updates for the Robot Operating System
336+ ros +yes enabled Security Updates for the Robot Operating System
337 """
338 And stdout matches regexp
339 """
340- esm-apps yes enabled UA Apps: Extended Security Maintenance \(ESM\)
341+ esm-apps +yes enabled UA Apps: Extended Security Maintenance \(ESM\)
342 """
343 And stdout matches regexp
344 """
345- esm-infra yes enabled UA Infra: Extended Security Maintenance \(ESM\)
346+ esm-infra +yes enabled UA Infra: Extended Security Maintenance \(ESM\)
347 """
348 When I run `ua detach` `with sudo` and stdin `y`
349 Then stdout matches regexp:
350@@ -911,3 +912,88 @@ Feature: Enable command behaviour when attached to an UA subscription
351 | release | ros-security-source | ros-updates-source |
352 | xenial | https://esm.ubuntu.com/ros/ubuntu xenial-security/main | https://esm.ubuntu.com/ros-updates/ubuntu xenial-updates/main |
353 | bionic | https://esm.ubuntu.com/ros/ubuntu bionic-security/main | https://esm.ubuntu.com/ros-updates/ubuntu bionic-updates/main |
354+
355+ # Overall test for overrides; in the future, when many services
356+ # have overrides, we can consider removing this
357+ # FIPS is a good choice because we expect to have it
358+ @series.focal
359+ @uses.config.machine_type.aws.generic
360+ Scenario: Cloud overrides for a generic aws Focal instance
361+ Given a `focal` machine with ubuntu-advantage-tools installed
362+ When I create the file `/tmp/machine-token-overlay.json` with the following:
363+ """
364+ {
365+ "machineTokenInfo": {
366+ "contractInfo": {
367+ "resourceEntitlements": [
368+ {
369+ "type": "fips",
370+ "entitled": true,
371+ "affordances": {
372+ "architectures": [
373+ "amd64",
374+ "ppc64el",
375+ "ppc64le",
376+ "s390x",
377+ "x86_64"
378+ ],
379+ "series": [
380+ "xenial",
381+ "bionic",
382+ "focal"
383+ ]
384+ },
385+ "directives": {
386+ "additionalPackages": [
387+ "ubuntu-fips"
388+ ],
389+ "aptKey": "E23341B2A1467EDBF07057D6C1997C40EDE22758",
390+ "aptURL": "https://esm.ubuntu.com/fips",
391+ "suites": [
392+ "xenial",
393+ "bionic",
394+ "focal"
395+ ]
396+ },
397+ "obligations": {
398+ "enableByDefault": false
399+ },
400+ "overrides": [
401+ {
402+ "selector": {
403+ "series": "focal"
404+ },
405+ "directives": {
406+ "additionalPackages": [
407+ "some-package-focal"
408+ ]
409+ }
410+ },
411+ {
412+ "selector": {
413+ "cloud": "aws"
414+ },
415+ "directives": {
416+ "additionalPackages": [
417+ "some-package-aws"
418+ ]
419+ }
420+ }
421+ ]
422+ }
423+ ]
424+ }
425+ }
426+ }
427+ """
428+ And I append the following on uaclient config:
429+ """
430+ features:
431+ machine_token_overlay: "/tmp/machine-token-overlay.json"
432+ """
433+ And I attach `contract_token` with sudo
434+ And I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
435+ Then stderr matches regexp:
436+ """
437+ Stderr: E: Unable to locate package some-package-aws
438+ """
439diff --git a/features/aws-ids.yaml b/features/aws-ids.yaml
440index f2376ad..ec6c43c 100644
441--- a/features/aws-ids.yaml
442+++ b/features/aws-ids.yaml
443@@ -3,3 +3,4 @@ bionic-fips: ami-03b75f613f80bcff1
444 focal: ami-0489b8bdbbf3a3b32
445 xenial: ami-011bcfe2bea365b6a
446 xenial-fips: ami-077e4c339a098fc9f
447+focal-fips: ami-02782bf2569bf457c
448diff --git a/features/azure-ids.yaml b/features/azure-ids.yaml
449index 735223f..10b1155 100644
450--- a/features/azure-ids.yaml
451+++ b/features/azure-ids.yaml
452@@ -3,3 +3,4 @@ focal: "Canonical:0001-com-ubuntu-pro-focal:pro-20_04-lts"
453 xenial: "Canonical:0001-com-ubuntu-pro-xenial:pro-16_04-lts"
454 bionic-fips: "Canonical:0001-com-ubuntu-pro-bionic-fips:pro-fips-18_04"
455 xenial-fips: "Canonical:0001-com-ubuntu-pro-xenial-fips:pro-fips-16_04-private"
456+focal-fips: "Canonical:0001-com-ubuntu-pro-focal-fips:pro-fips-20_04"
457diff --git a/features/enable_fips_vm.feature b/features/enable_fips_vm.feature
458index a055354..03ccd39 100644
459--- a/features/enable_fips_vm.feature
460+++ b/features/enable_fips_vm.feature
461@@ -462,14 +462,11 @@ Feature: FIPS enablement in lxd VMs
462 | focal | FIPS Updates | fips-updates |https://esm.ubuntu.com/fips-updates/ubuntu focal-updates/main |
463
464 @slow
465- @series.xenial
466- @series.bionic
467+ @series.lts
468 @uses.config.machine_type.lxd.vm
469 Scenario Outline: Attached enable fips-updates on fips enabled vm
470 Given a `<release>` machine with ubuntu-advantage-tools installed
471 When I attach `contract_token` with sudo
472- And I run `ua disable livepatch` with sudo
473- And I run `DEBIAN_FRONTEND=noninteractive apt-get install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y openssh-client openssh-server strongswan` with sudo, retrying exit [100]
474 And I run `ua enable fips --assume-yes` with sudo
475 Then stdout matches regexp:
476 """
477@@ -484,47 +481,61 @@ Feature: FIPS enablement in lxd VMs
478 fips +yes enabled
479 """
480 When I reboot the `<release>` machine
481- And I run `ua enable fips-updates --assume-yes` with sudo
482+ And I run `uname -r` as non-root
483 Then stdout matches regexp:
484 """
485- Updating package lists
486- Installing FIPS Updates packages
487- FIPS Updates enabled
488- A reboot is required to complete install
489+ fips
490 """
491- When I run `ua status --all` with sudo
492+ When I verify that running `ua enable fips-updates --assume-yes` `with sudo` exits `0`
493 Then stdout matches regexp:
494 """
495- fips +yes n/a
496- """
497- And stdout matches regexp:
498- """
499- fips-updates +yes enabled
500- """
501- When I reboot the `<release>` machine
502- Then I verify that running `apt update` `with sudo` exits `0`
503- And I verify that `ubuntu-fips` is installed from apt source `<fips-apt-source>`
504- And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
505- And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
506- And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
507- And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
508- And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
509- And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
510- When I run `uname -r` as non-root
511- Then stdout matches regexp:
512- """
513- fips
514+ One moment, checking your subscription first
515+ Disabling incompatible service: FIPS
516+ Updating package lists
517+ Installing FIPS Updates packages
518+ FIPS Updates enabled
519+ A reboot is required to complete install.
520+ """
521+ When I run `ua status --all` with sudo
522+ Then stdout matches regexp:
523+ """
524+ fips-updates +yes enabled
525+ """
526+ And stdout matches regexp:
527+ """
528+ fips +yes n/a
529+ """
530+ When I reboot the `<release>` machine
531+ And I run `ua enable livepatch` with sudo
532+ And I run `ua status --all` with sudo
533+ Then stdout matches regexp:
534+ """
535+ fips-updates +yes enabled
536+ """
537+ And stdout matches regexp:
538+ """
539+ fips +yes n/a
540+ """
541+ And stdout matches regexp:
542+ """
543+ livepatch +yes enabled
544+ """
545+ When I run `uname -r` as non-root
546+ Then stdout matches regexp:
547+ """
548+ fips
549+ """
550+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
551+ Then I will see the following on stdout:
552+ """
553+ 1
554 """
555- When I run `cat /proc/sys/crypto/fips_enabled` with sudo
556- Then I will see the following on stdout:
557- """
558- 1
559- """
560
561 Examples: ubuntu release
562- | release | fips-apt-source |
563- | xenial | https://esm.ubuntu.com/fips-updates/ubuntu xenial-updates/main |
564- | bionic | https://esm.ubuntu.com/fips-updates/ubuntu bionic-updates/main |
565+ | release |
566+ | xenial |
567+ | bionic |
568+ | focal |
569
570 @slow
571 @series.xenial
572diff --git a/features/realtime_kernel.feature b/features/realtime_kernel.feature
573new file mode 100644
574index 0000000..a04664c
575--- /dev/null
576+++ b/features/realtime_kernel.feature
577@@ -0,0 +1,128 @@
578+@uses.config.contract_token
579+Feature: Enable command behaviour when attached to an UA subscription
580+
581+ @series.jammy
582+ @uses.config.machine_type.lxd.container
583+ Scenario Outline: Enable Real-Time Kernel service in a container
584+ Given a `<release>` machine with ubuntu-advantage-tools installed
585+ When I attach `contract_token` with sudo
586+ Then I verify that running `ua enable realtime-kernel` `as non-root` exits `1`
587+ And I will see the following on stderr:
588+ """
589+ This command must be run as root (try using sudo).
590+ """
591+ Then I verify that running `ua enable realtime-kernel --beta` `with sudo` exits `1`
592+ Then I will see the following on stdout:
593+ """
594+ One moment, checking your subscription first
595+ Cannot install Real-Time Kernel on a container.
596+ """
597+ Examples: ubuntu release
598+ | release |
599+ | jammy |
600+
601+ @series.lts
602+ @uses.config.machine_type.lxd.vm
603+ Scenario Outline: Enable Real-Time Kernel service on unsupported release
604+ Given a `<release>` machine with ubuntu-advantage-tools installed
605+ When I attach `contract_token` with sudo
606+ Then I verify that running `ua enable realtime-kernel` `as non-root` exits `1`
607+ And I will see the following on stderr:
608+ """
609+ This command must be run as root (try using sudo).
610+ """
611+ Then I verify that running `ua enable realtime-kernel --beta` `with sudo` exits `1`
612+ Then I will see the following on stdout:
613+ """
614+ One moment, checking your subscription first
615+ Real-Time Kernel is not available for Ubuntu <version> (<full_name>).
616+ """
617+ Examples: ubuntu release
618+ | release | version | full_name |
619+ | xenial | 16.04 LTS | Xenial Xerus |
620+ | bionic | 18.04 LTS | Bionic Beaver |
621+ | focal | 20.04 LTS | Focal Fossa |
622+ | jammy | 22.04 | Jammy Jellyfish |
623+
624+ @series.jammy
625+ @uses.config.machine_type.gcp.generic
626+ Scenario Outline: Enable Real-Time Kernel service
627+ Given a `<release>` machine with ubuntu-advantage-tools installed
628+ When I create the file `/home/ubuntu/machine-token-overlay.json` with the following:
629+ """
630+ {
631+ "machineTokenInfo": {
632+ "contractInfo": {
633+ "resourceEntitlements": [
634+ {
635+ "type": "realtime-kernel",
636+ "affordances": {
637+ "series": ["jammy"]
638+ }
639+ }
640+ ]
641+ }
642+ }
643+ }
644+ """
645+ And I append the following on uaclient config:
646+ """
647+ features:
648+ machine_token_overlay: "/home/ubuntu/machine-token-overlay.json"
649+ """
650+ When I attach `contract_token` with sudo
651+ And I run `ua disable livepatch --assume-yes` with sudo
652+ Then I verify that running `ua enable realtime-kernel` `as non-root` exits `1`
653+ And I will see the following on stderr:
654+ """
655+ This command must be run as root (try using sudo).
656+ """
657+ Then I verify that running `ua enable realtime-kernel` `with sudo` exits `1`
658+ And stderr matches regexp:
659+ """
660+ Cannot enable unknown service 'realtime-kernel'.
661+ """
662+ When I run `ua enable realtime-kernel --beta` `with sudo` and stdin `y`
663+ Then stdout matches regexp:
664+ """
665+ One moment, checking your subscription first
666+ The real-time kernel is a beta version of the 22.04 Ubuntu kernel with the
667+ PREEMPT_RT patchset integrated for x86_64 and ARM64.
668+
669+ .*You will not be able to revert to your original kernel after enabling real-time..*
670+
671+ Do you want to continue\? \[ default = Yes \]: \(Y/n\) Updating package lists
672+ Installing Real-Time Kernel packages
673+ Real-Time Kernel enabled
674+ A reboot is required to complete install.
675+ """
676+ When I run `apt-cache policy linux-realtime` as non-root
677+ Then stdout does not match regexp:
678+ """
679+ .*Installed: \(none\)
680+ """
681+ And stdout matches regexp:
682+ """
683+ \s* 500 https://esm.ubuntu.com/realtime/ubuntu <release>/main amd64 Packages
684+ """
685+ When I verify that running `ua enable realtime-kernel --beta` `with sudo` exits `1`
686+ Then stdout matches regexp
687+ """
688+ One moment, checking your subscription first
689+ Real-Time Kernel is already enabled.
690+ See: sudo ua status
691+ """
692+ When I reboot the `<release>` machine
693+ When I run `uname -r` as non-root
694+ Then stdout matches regexp:
695+ """
696+ realtime
697+ """
698+ When I run `ua disable realtime-kernel` `with sudo` and stdin `y`
699+ Then stdout matches regexp:
700+ """
701+ This will disable the Real-Time Kernel entitlement but the Real-Time Kernel will remain installed.
702+ """
703+ Examples: ubuntu release
704+ | release |
705+ | jammy |
706diff --git a/features/staging_commands.feature b/features/staging_commands.feature
707index 360b329..23aed73 100644
708--- a/features/staging_commands.feature
709+++ b/features/staging_commands.feature
710@@ -9,7 +9,7 @@ Feature: Enable command behaviour when attached to an UA staging subscription
711 And I run `ua status --all` as non-root
712 Then stdout matches regexp
713 """
714- esm-apps yes enabled UA Apps: Extended Security Maintenance \(ESM\)
715+ esm-apps +yes enabled UA Apps: Extended Security Maintenance \(ESM\)
716 """
717 And I verify that running `apt update` `with sudo` exits `0`
718 When I run `apt-cache policy` as non-root
719diff --git a/features/ubuntu_pro.feature b/features/ubuntu_pro.feature
720index 04ef4fc..0f336ef 100644
721--- a/features/ubuntu_pro.feature
722+++ b/features/ubuntu_pro.feature
723@@ -25,7 +25,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
724 When I run `ua auto-attach` with sudo
725 Then stdout matches regexp:
726 """
727- SERVICE ENTITLED STATUS DESCRIPTION
728+ SERVICE +ENTITLED STATUS DESCRIPTION
729 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
730 """
731 Then stdout matches regexp:
732@@ -96,7 +96,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
733 When I run `ua auto-attach` with sudo
734 Then stdout matches regexp:
735 """
736- SERVICE ENTITLED STATUS DESCRIPTION
737+ SERVICE +ENTITLED STATUS DESCRIPTION
738 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
739 """
740 Then stdout matches regexp:
741@@ -167,7 +167,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
742 When I run `ua auto-attach` with sudo
743 Then stdout matches regexp:
744 """
745- SERVICE ENTITLED STATUS DESCRIPTION
746+ SERVICE +ENTITLED STATUS DESCRIPTION
747 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
748 """
749 Then stdout matches regexp:
750@@ -228,7 +228,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
751 And I run `ua status --wait` as non-root
752 Then stdout matches regexp:
753 """
754- SERVICE ENTITLED STATUS DESCRIPTION
755+ SERVICE +ENTITLED STATUS DESCRIPTION
756 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
757 """
758 Then stdout matches regexp:
759@@ -246,7 +246,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
760 When I run `ua status --all` as non-root
761 Then stdout matches regexp:
762 """
763- SERVICE ENTITLED STATUS DESCRIPTION
764+ SERVICE +ENTITLED STATUS DESCRIPTION
765 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
766 """
767 Then stdout matches regexp:
768@@ -362,7 +362,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
769 And I run `ua status` as non-root
770 Then stdout matches regexp:
771 """
772- SERVICE ENTITLED STATUS DESCRIPTION
773+ SERVICE +ENTITLED STATUS DESCRIPTION
774 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
775 """
776 Then stdout matches regexp:
777@@ -380,7 +380,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
778 When I run `ua status --all` as non-root
779 Then stdout matches regexp:
780 """
781- SERVICE ENTITLED STATUS DESCRIPTION
782+ SERVICE +ENTITLED STATUS DESCRIPTION
783 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
784 """
785 Then stdout matches regexp:
786@@ -496,7 +496,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
787 And I run `ua status` as non-root
788 Then stdout matches regexp:
789 """
790- SERVICE ENTITLED STATUS DESCRIPTION
791+ SERVICE +ENTITLED STATUS DESCRIPTION
792 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
793 """
794 Then stdout matches regexp:
795@@ -514,7 +514,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
796 When I run `ua status --all` as non-root
797 Then stdout matches regexp:
798 """
799- SERVICE ENTITLED STATUS DESCRIPTION
800+ SERVICE +ENTITLED STATUS DESCRIPTION
801 cc-eal +yes +<cc-eal-s> +Common Criteria EAL2 Provisioning Packages
802 """
803 Then stdout matches regexp:
804diff --git a/features/ubuntu_pro_fips.feature b/features/ubuntu_pro_fips.feature
805index 7a18b03..1e83ee3 100644
806--- a/features/ubuntu_pro_fips.feature
807+++ b/features/ubuntu_pro_fips.feature
808@@ -26,12 +26,6 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
809 """
810 And I verify that running `apt update` `with sudo` exits `0`
811 And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
812- And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
813- And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
814- And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
815- And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
816- And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
817- And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
818 When I run `uname -r` as non-root
819 Then stdout matches regexp:
820 """
821@@ -79,6 +73,10 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
822 """
823 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
824 """
825+ And apt-cache policy for the following url has permission `1001`
826+ """
827+ <fips-apt-source> amd64 Packages
828+ """
829 And I verify that running `apt update` `with sudo` exits `0`
830 When I run `apt install -y <infra-pkg>/<release>-infra-security` with sudo, retrying exit [100]
831 And I run `apt-cache policy <infra-pkg>` as non-root
832@@ -99,15 +97,48 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
833 \s*\*\*\* .* 500
834 \s*500 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
835 """
836+ When I run `ua enable fips-updates --assume-yes` with sudo
837+ Then I will see the following on stdout:
838+ """
839+ One moment, checking your subscription first
840+ Disabling incompatible service: FIPS
841+ Updating package lists
842+ Installing FIPS Updates packages
843+ FIPS Updates enabled
844+ A reboot is required to complete install.
845+ """
846+ When I run `ua status` with sudo
847+ Then stdout matches regexp:
848+ """
849+ fips +yes +n/a +NIST-certified core packages
850+ fips-updates +yes +enabled +NIST-certified core packages with priority security updates
851+ """
852+ When I reboot the `<release>` machine
853+ And I run `uname -r` as non-root
854+ Then stdout matches regexp:
855+ """
856+ <fips-kernel-version>
857+ """
858+ When I run `apt-cache policy ubuntu-azure-fips` as non-root
859+ Then stdout does not match regexp:
860+ """
861+ .*Installed: \(none\)
862+ """
863+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
864+ Then I will see the following on stdout:
865+ """
866+ 1
867+ """
868
869 Examples: ubuntu release
870 | release | infra-pkg | apps-pkg | fips-apt-source | fips-kernel-version |
871 | xenial | libkrad0 | jq | https://esm.ubuntu.com/fips/ubuntu xenial/main | fips |
872 | bionic | libkrad0 | bundler | https://esm.ubuntu.com/fips/ubuntu bionic/main | azure-fips |
873+ | focal | hello | 389-ds | https://esm.ubuntu.com/fips/ubuntu focal/main | azure-fips |
874
875- @series.lts
876- @uses.config.machine_type.aws.pro.fips
877- Scenario Outline: Check fips is enabled correctly on Ubuntu pro fips AWS machine
878+ @series.focal
879+ @uses.config.machine_type.azure.pro.fips
880+ Scenario Outline: Check fips packages are correctly installed on Azure Focal machine
881 Given a `<release>` machine with ubuntu-advantage-tools installed
882 When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
883 """
884@@ -115,6 +146,44 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
885 data_dir: /var/lib/ubuntu-advantage
886 log_level: debug
887 log_file: /var/log/ubuntu-advantage.log
888+ features:
889+ allow_xenial_fips_on_cloud: true
890+ """
891+ And I run `ua auto-attach` with sudo
892+ And I run `ua status --wait` as non-root
893+ And I run `ua status` as non-root
894+ Then stdout matches regexp:
895+ """
896+ esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
897+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
898+ fips +yes +enabled +NIST-certified core packages
899+ fips-updates +yes +disabled +NIST-certified core packages with priority security updates
900+ livepatch +yes +n/a +Canonical Livepatch service
901+ """
902+ And I verify that running `apt update` `with sudo` exits `0`
903+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
904+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
905+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
906+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
907+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
908+
909+ Examples: ubuntu release
910+ | release | fips-apt-source |
911+ | focal | https://esm.ubuntu.com/fips/ubuntu focal/main |
912+
913+ @series.xenial
914+ @series.bionic
915+ @uses.config.machine_type.azure.pro.fips
916+ Scenario Outline: Check fips packages are correctly installed on Azure Bionic & Xenial machines
917+ Given a `<release>` machine with ubuntu-advantage-tools installed
918+ When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
919+ """
920+ contract_url: 'https://contracts.canonical.com'
921+ data_dir: /var/lib/ubuntu-advantage
922+ log_level: debug
923+ log_file: /var/log/ubuntu-advantage.log
924+ features:
925+ allow_xenial_fips_on_cloud: true
926 """
927 And I run `ua auto-attach` with sudo
928 And I run `ua status --wait` as non-root
929@@ -135,6 +204,36 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
930 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
931 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
932 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
933+
934+ Examples: ubuntu release
935+ | release | fips-apt-source |
936+ | xenial | https://esm.ubuntu.com/fips/ubuntu xenial/main |
937+ | bionic | https://esm.ubuntu.com/fips/ubuntu bionic/main |
938+
939+ @series.lts
940+ @uses.config.machine_type.aws.pro.fips
941+ Scenario Outline: Check fips is enabled correctly on Ubuntu pro fips AWS machine
942+ Given a `<release>` machine with ubuntu-advantage-tools installed
943+ When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
944+ """
945+ contract_url: 'https://contracts.canonical.com'
946+ data_dir: /var/lib/ubuntu-advantage
947+ log_level: debug
948+ log_file: /var/log/ubuntu-advantage.log
949+ """
950+ And I run `ua auto-attach` with sudo
951+ And I run `ua status --wait` as non-root
952+ And I run `ua status` as non-root
953+ Then stdout matches regexp:
954+ """
955+ esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
956+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
957+ fips +yes +enabled +NIST-certified core packages
958+ fips-updates +yes +disabled +NIST-certified core packages with priority security updates
959+ livepatch +yes +n/a +Canonical Livepatch service
960+ """
961+ And I verify that running `apt update` `with sudo` exits `0`
962+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
963 When I run `uname -r` as non-root
964 Then stdout matches regexp:
965 """
966@@ -182,6 +281,10 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
967 """
968 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
969 """
970+ And apt-cache policy for the following url has permission `1001`
971+ """
972+ <fips-apt-source> amd64 Packages
973+ """
974 And I verify that running `apt update` `with sudo` exits `0`
975 When I run `apt install -y <infra-pkg>/<release>-infra-security` with sudo, retrying exit [100]
976 And I run `apt-cache policy <infra-pkg>` as non-root
977@@ -202,9 +305,167 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
978 \s*\*\*\* .* 500
979 \s*500 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
980 """
981+ When I run `ua enable fips-updates --assume-yes` with sudo
982+ Then I will see the following on stdout:
983+ """
984+ One moment, checking your subscription first
985+ Disabling incompatible service: FIPS
986+ Updating package lists
987+ Installing FIPS Updates packages
988+ FIPS Updates enabled
989+ A reboot is required to complete install.
990+ """
991+ When I run `ua status` with sudo
992+ Then stdout matches regexp:
993+ """
994+ fips +yes +n/a +NIST-certified core packages
995+ fips-updates +yes +enabled +NIST-certified core packages with priority security updates
996+ """
997+ When I reboot the `<release>` machine
998+ And I run `uname -r` as non-root
999+ Then stdout matches regexp:
1000+ """
1001+ <fips-kernel-version>
1002+ """
1003+ When I run `apt-cache policy ubuntu-aws-fips` as non-root
1004+ Then stdout does not match regexp:
1005+ """
1006+ .*Installed: \(none\)
1007+ """
1008+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
1009+ Then I will see the following on stdout:
1010+ """
1011+ 1
1012+ """
1013
1014 Examples: ubuntu release
1015 | release | infra-pkg | apps-pkg | fips-apt-source | fips-kernel-version |
1016 | xenial | libkrad0 | jq | https://esm.ubuntu.com/fips/ubuntu xenial/main | fips |
1017 | bionic | libkrad0 | bundler | https://esm.ubuntu.com/fips/ubuntu bionic/main | aws-fips |
1018+ | focal | hello | 389-ds | https://esm.ubuntu.com/fips/ubuntu focal/main | aws-fips |
1019
1020+ @series.focal
1021+ @uses.config.machine_type.aws.pro.fips
1022+ Scenario Outline: Check fips packages are correctly installed on AWS Focal machine
1023+ Given a `<release>` machine with ubuntu-advantage-tools installed
1024+ When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
1025+ """
1026+ contract_url: 'https://contracts.canonical.com'
1027+ data_dir: /var/lib/ubuntu-advantage
1028+ log_level: debug
1029+ log_file: /var/log/ubuntu-advantage.log
1030+ features:
1031+ allow_xenial_fips_on_cloud: true
1032+ """
1033+ And I run `ua auto-attach` with sudo
1034+ And I run `ua status --wait` as non-root
1035+ And I run `ua status` as non-root
1036+ Then stdout matches regexp:
1037+ """
1038+ esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
1039+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1040+ fips +yes +enabled +NIST-certified core packages
1041+ fips-updates +yes +disabled +NIST-certified core packages with priority security updates
1042+ livepatch +yes +n/a +Canonical Livepatch service
1043+ """
1044+ And I verify that running `apt update` `with sudo` exits `0`
1045+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
1046+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
1047+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
1048+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
1049+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
1050+
1051+ Examples: ubuntu release
1052+ | release | fips-apt-source |
1053+ | focal | https://esm.ubuntu.com/fips/ubuntu focal/main |
1054+
1055+ @series.xenial
1056+ @series.bionic
1057+ @uses.config.machine_type.aws.pro.fips
1058+ Scenario Outline: Check fips packages are correctly installed on AWS Bionic & Xenial machines
1059+ Given a `<release>` machine with ubuntu-advantage-tools installed
1060+ When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
1061+ """
1062+ contract_url: 'https://contracts.canonical.com'
1063+ data_dir: /var/lib/ubuntu-advantage
1064+ log_level: debug
1065+ log_file: /var/log/ubuntu-advantage.log
1066+ features:
1067+ allow_xenial_fips_on_cloud: true
1068+ """
1069+ And I run `ua auto-attach` with sudo
1070+ And I run `ua status --wait` as non-root
1071+ And I run `ua status` as non-root
1072+ Then stdout matches regexp:
1073+ """
1074+ esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
1075+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1076+ fips +yes +enabled +NIST-certified core packages
1077+ fips-updates +yes +disabled +NIST-certified core packages with priority security updates
1078+ livepatch +yes +n/a +Canonical Livepatch service
1079+ """
1080+ And I verify that running `apt update` `with sudo` exits `0`
1081+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
1082+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
1083+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
1084+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
1085+ And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
1086+ And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
1087+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
1088+
1089+ Examples: ubuntu release
1090+ | release | fips-apt-source |
1091+ | xenial | https://esm.ubuntu.com/fips/ubuntu xenial/main |
1092+ | bionic | https://esm.ubuntu.com/fips/ubuntu bionic/main |
1093+
1094+ @series.focal
1095+ @uses.config.machine_type.azure.pro.fips
1096+ @uses.config.machine_type.aws.pro.fips
1097+ Scenario Outline: Check fips-updates can be enable in a focal PRO FIPS machine
1098+ Given a `<release>` machine with ubuntu-advantage-tools installed
1099+ When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
1100+ """
1101+ contract_url: 'https://contracts.canonical.com'
1102+ data_dir: /var/lib/ubuntu-advantage
1103+ log_level: debug
1104+ log_file: /var/log/ubuntu-advantage.log
1105+ """
1106+ And I run `ua auto-attach` with sudo
1107+ And I run `ua status --wait` as non-root
1108+ And I run `ua status` as non-root
1109+ Then stdout matches regexp:
1110+ """
1111+ fips +yes +enabled +NIST-certified core packages
1112+ fips-updates +yes +disabled +NIST-certified core packages with priority security updates
1113+ """
1114+ When I run `ua enable fips-updates --assume-yes` with sudo
1115+ Then stdout matches regexp:
1116+ """
1117+ One moment, checking your subscription first
1118+ Disabling incompatible service: FIPS
1119+ Updating package lists
1120+ Installing FIPS Updates packages
1121+ FIPS Updates enabled
1122+ A reboot is required to complete install.
1123+ """
1124+ When I run `ua status` with sudo
1125+ Then stdout matches regexp:
1126+ """
1127+ fips +yes +n/a +NIST-certified core packages
1128+ fips-updates +yes +enabled +NIST-certified core packages with priority security updates
1129+ """
1130+ When I reboot the `<release>` machine
1131+ And I run `uname -r` as non-root
1132+ Then stdout matches regexp:
1133+ """
1134+ fips
1135+ """
1136+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
1137+ Then I will see the following on stdout:
1138+ """
1139+ 1
1140+ """
1141+
1142+ Examples: ubuntu release
1143+ | release |
1144+ | focal |
1145diff --git a/features/ubuntu_upgrade.feature b/features/ubuntu_upgrade.feature
1146index 4cf3407..0ffa683 100644
1147--- a/features/ubuntu_upgrade.feature
1148+++ b/features/ubuntu_upgrade.feature
1149@@ -17,7 +17,6 @@ Feature: Upgrade between releases when uaclient is attached
1150 [Sources]
1151 AllowThirdParty=yes
1152 """
1153- And I run `sed -i 's/Prompt=lts/Prompt=normal/' /etc/update-manager/release-upgrades` with sudo
1154 And I run `do-release-upgrade <devel_release> --frontend DistUpgradeViewNonInteractive` `with sudo` and stdin `y\n`
1155 And I reboot the `<release>` machine
1156 And I run `lsb_release -cs` as non-root
1157@@ -42,7 +41,7 @@ Feature: Upgrade between releases when uaclient is attached
1158
1159 Examples: ubuntu release
1160 | release | next_release | devel_release |
1161- | focal | impish | |
1162+ | focal | jammy | --devel-release |
1163 | impish | jammy | --devel-release |
1164
1165 @slow
1166diff --git a/features/unattached_status.feature b/features/unattached_status.feature
1167index 08538a2..eccaf60 100644
1168--- a/features/unattached_status.feature
1169+++ b/features/unattached_status.feature
1170@@ -47,13 +47,13 @@ Feature: Unattached status
1171 When I run `ua status` as non-root
1172 Then stdout matches regexp:
1173 """
1174- SERVICE AVAILABLE DESCRIPTION
1175- cc-eal <cc-eal> +Common Criteria EAL2 Provisioning Packages
1176+ SERVICE +AVAILABLE DESCRIPTION
1177+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
1178 ?<cis>( +<cis-available> +Security compliance and audit tools)?
1179- ?esm-infra <esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1180- fips <fips> +NIST-certified core packages
1181- fips-updates <fips> +NIST-certified core packages with priority security updates
1182- livepatch <livepatch> +Canonical Livepatch service
1183+ ?esm-infra +<esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1184+ fips +<fips> +NIST-certified core packages
1185+ fips-updates +<fips> +NIST-certified core packages with priority security updates
1186+ livepatch +<livepatch> +Canonical Livepatch service
1187 ?<usg>( +<cis-available> +Security compliance and audit tools)?
1188
1189 This machine is not attached to a UA subscription.
1190@@ -62,16 +62,17 @@ Feature: Unattached status
1191 When I run `ua status --all` as non-root
1192 Then stdout matches regexp:
1193 """
1194- SERVICE AVAILABLE DESCRIPTION
1195- cc-eal <cc-eal> +Common Criteria EAL2 Provisioning Packages
1196+ SERVICE +AVAILABLE DESCRIPTION
1197+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
1198 ?<cis>( +<cis-available> +Security compliance and audit tools)?
1199- ?esm-apps <esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1200- esm-infra <esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1201- fips <fips> +NIST-certified core packages
1202- fips-updates <fips> +NIST-certified core packages with priority security updates
1203- livepatch <livepatch> +Canonical Livepatch service
1204- ros <ros> +Security Updates for the Robot Operating System
1205- ros-updates <ros> +All Updates for the Robot Operating System
1206+ ?esm-apps +<esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1207+ esm-infra +<esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1208+ fips +<fips> +NIST-certified core packages
1209+ fips-updates +<fips> +NIST-certified core packages with priority security updates
1210+ livepatch +<livepatch> +Canonical Livepatch service
1211+ realtime-kernel +<realtime-kernel> +Beta-version Ubuntu Kernel with PREEMPT_RT patches
1212+ ros +<ros> +Security Updates for the Robot Operating System
1213+ ros-updates +<ros> +All Updates for the Robot Operating System
1214 ?<usg>( +<cis-available> +Security compliance and audit tools)?
1215
1216 This machine is not attached to a UA subscription.
1217@@ -80,13 +81,13 @@ Feature: Unattached status
1218 When I run `ua status` with sudo
1219 Then stdout matches regexp:
1220 """
1221- SERVICE AVAILABLE DESCRIPTION
1222- cc-eal <cc-eal> +Common Criteria EAL2 Provisioning Packages
1223+ SERVICE +AVAILABLE DESCRIPTION
1224+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
1225 ?<cis>( +<cis-available> +Security compliance and audit tools)?
1226- ?esm-infra <esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1227- fips <fips> +NIST-certified core packages
1228- fips-updates <fips> +NIST-certified core packages with priority security updates
1229- livepatch <livepatch> +Canonical Livepatch service
1230+ ?esm-infra +<esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1231+ fips +<fips> +NIST-certified core packages
1232+ fips-updates +<fips> +NIST-certified core packages with priority security updates
1233+ livepatch +<livepatch> +Canonical Livepatch service
1234 ?<usg>( +<cis-available> +Security compliance and audit tools)?
1235
1236 This machine is not attached to a UA subscription.
1237@@ -95,16 +96,17 @@ Feature: Unattached status
1238 When I run `ua status --all` with sudo
1239 Then stdout matches regexp:
1240 """
1241- SERVICE AVAILABLE DESCRIPTION
1242- cc-eal <cc-eal> +Common Criteria EAL2 Provisioning Packages
1243+ SERVICE +AVAILABLE DESCRIPTION
1244+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
1245 ?<cis>( +<cis-available> +Security compliance and audit tools)?
1246- ?esm-apps <esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1247- esm-infra <esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1248- fips <fips> +NIST-certified core packages
1249- fips-updates <fips> +NIST-certified core packages with priority security updates
1250- livepatch <livepatch> +Canonical Livepatch service
1251- ros <ros> +Security Updates for the Robot Operating System
1252- ros-updates <ros> +All Updates for the Robot Operating System
1253+ ?esm-apps +<esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1254+ esm-infra +<esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1255+ fips +<fips> +NIST-certified core packages
1256+ fips-updates +<fips> +NIST-certified core packages with priority security updates
1257+ livepatch +<livepatch> +Canonical Livepatch service
1258+ realtime-kernel +<realtime-kernel> +Beta-version Ubuntu Kernel with PREEMPT_RT patches
1259+ ros +<ros> +Security Updates for the Robot Operating System
1260+ ros-updates +<ros> +All Updates for the Robot Operating System
1261 ?<usg>( +<cis-available> +Security compliance and audit tools)?
1262
1263 This machine is not attached to a UA subscription.
1264@@ -118,16 +120,17 @@ Feature: Unattached status
1265 And I run `ua status` as non-root
1266 Then stdout matches regexp:
1267 """
1268- SERVICE AVAILABLE DESCRIPTION
1269- cc-eal <cc-eal> +Common Criteria EAL2 Provisioning Packages
1270+ SERVICE +AVAILABLE DESCRIPTION
1271+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
1272 ?<cis>( +<cis-available> +Security compliance and audit tools)?
1273- ?esm-apps <esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1274- esm-infra <esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1275- fips <fips> +NIST-certified core packages
1276- fips-updates <fips> +NIST-certified core packages with priority security updates
1277- livepatch <livepatch> +Canonical Livepatch service
1278- ros <ros> +Security Updates for the Robot Operating System
1279- ros-updates <ros> +All Updates for the Robot Operating System
1280+ ?esm-apps +<esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
1281+ esm-infra +<esm-infra> +UA Infra: Extended Security Maintenance \(ESM\)
1282+ fips +<fips> +NIST-certified core packages
1283+ fips-updates +<fips> +NIST-certified core packages with priority security updates
1284+ livepatch +<livepatch> +Canonical Livepatch service
1285+ realtime-kernel +<realtime-kernel> +Beta-version Ubuntu Kernel with PREEMPT_RT patches
1286+ ros +<ros> +Security Updates for the Robot Operating System
1287+ ros-updates +<ros> +All Updates for the Robot Operating System
1288 ?<usg>( +<cis-available> +Security compliance and audit tools)?
1289
1290 This machine is not attached to a UA subscription.
1291@@ -135,44 +138,44 @@ Feature: Unattached status
1292 """
1293
1294 Examples: ubuntu release
1295- | release | esm-apps | cc-eal | cis | cis-available | fips | esm-infra | ros | livepatch | usg |
1296- | xenial | yes | yes | cis | yes | yes | yes | yes | yes | |
1297- | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |
1298- | focal | yes | no | | yes | yes | yes | no | yes | usg |
1299- | impish | no | no | cis | no | no | no | no | no | |
1300- | jammy | no | no | cis | no | no | no | no | no | |
1301+ | release | esm-apps | cc-eal | cis | cis-available | fips | esm-infra | ros | livepatch | usg | realtime-kernel |
1302+ | xenial | yes | yes | cis | yes | yes | yes | yes | yes | | no |
1303+ | bionic | yes | yes | cis | yes | yes | yes | yes | yes | | no |
1304+ | focal | yes | no | | yes | yes | yes | no | yes | usg | no |
1305+ | impish | no | no | cis | no | no | no | no | no | | no |
1306+ | jammy | no | no | cis | no | no | no | no | no | | yes |
1307
1308 @series.all
1309 @uses.config.machine_type.lxd.container
1310 @uses.config.contract_token
1311- @uses.config.contract_token_staging_expired
1312 Scenario Outline: Simulate status in a ubuntu machine
1313 Given a `<release>` machine with ubuntu-advantage-tools installed
1314 When I do a preflight check for `contract_token` without the all flag
1315 Then stdout matches regexp:
1316 """
1317- SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1318- cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1319+ SERVICE +AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1320+ cc-eal +<cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1321 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
1322- ?esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1323- fips <fips> +yes +no +NIST-certified core packages
1324- fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
1325- livepatch <livepatch> +yes +yes +Canonical Livepatch service
1326+ ?esm-infra +<esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1327+ fips +<fips> +yes +no +NIST-certified core packages
1328+ fips-updates +<fips> +yes +no +NIST-certified core packages with priority security updates
1329+ livepatch +<livepatch> +yes +yes +Canonical Livepatch service
1330 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
1331 """
1332 When I do a preflight check for `contract_token` with the all flag
1333 Then stdout matches regexp:
1334 """
1335- SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1336- cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1337+ SERVICE +AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1338+ cc-eal +<cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1339 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
1340- ?esm-apps <esm-apps> +yes +yes +UA Apps: Extended Security Maintenance \(ESM\)
1341- esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1342- fips <fips> +yes +no +NIST-certified core packages
1343- fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
1344- livepatch <livepatch> +yes +yes +Canonical Livepatch service
1345- ros <ros> +yes +no +Security Updates for the Robot Operating System
1346- ros-updates <ros> +yes +no +All Updates for the Robot Operating System
1347+ ?esm-apps +<esm-apps> +yes +yes +UA Apps: Extended Security Maintenance \(ESM\)
1348+ esm-infra +<esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1349+ fips +<fips> +yes +no +NIST-certified core packages
1350+ fips-updates +<fips> +yes +no +NIST-certified core packages with priority security updates
1351+ livepatch +<livepatch> +yes +yes +Canonical Livepatch service
1352+ realtime-kernel +<realtime-kernel> +yes +no +Beta-version Ubuntu Kernel with PREEMPT_RT patches
1353+ ros +<ros> +yes +no +Security Updates for the Robot Operating System
1354+ ros-updates +<ros> +yes +no +All Updates for the Robot Operating System
1355 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
1356 """
1357 When I do a preflight check for `contract_token` formatted as json
1358@@ -199,6 +202,20 @@ Feature: Unattached status
1359 services: []
1360 warnings: []
1361 """
1362+ Examples: ubuntu release
1363+ | release | esm-apps | cc-eal | cis | cis-available | fips | esm-infra | ros | livepatch | usg | realtime-kernel |
1364+ | xenial | yes | yes | cis | yes | yes | yes | yes | yes | | no |
1365+ | bionic | yes | yes | cis | yes | yes | yes | yes | yes | | no |
1366+ | focal | yes | no | | yes | yes | yes | no | yes | usg | no |
1367+ | impish | no | no | cis | no | no | no | no | no | | no |
1368+ | jammy | no | no | cis | no | no | no | no | no | | yes |
1369+
1370+
1371+ @series.all
1372+ @uses.config.machine_type.lxd.container
1373+ @uses.config.contract_token_staging_expired
1374+ Scenario Outline: Simulate status with expired token in a ubuntu machine
1375+ Given a `<release>` machine with ubuntu-advantage-tools installed
1376 When I run `sed -i 's/contracts.can/contracts.staging.can/' /etc/ubuntu-advantage/uaclient.conf` with sudo
1377 And I verify that a preflight check for `contract_token_staging_expired` formatted as json exits 1
1378 Then stdout is a json matching the `ua_status` schema
1379@@ -223,13 +240,13 @@ Feature: Unattached status
1380 This token is not valid.
1381 Contract \".*\" expired on .*
1382
1383- SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1384- cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1385+ SERVICE +AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
1386+ cc-eal +<cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
1387 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
1388- ?esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1389- fips <fips> +yes +no +NIST-certified core packages
1390- fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
1391- livepatch <livepatch> +yes +yes +Canonical Livepatch service
1392+ ?esm-infra +<esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
1393+ fips +<fips> +yes +no +NIST-certified core packages
1394+ fips-updates +<fips> +yes +no +NIST-certified core packages with priority security updates
1395+ livepatch +<livepatch> +yes +yes +Canonical Livepatch service
1396 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
1397 """
1398
1399@@ -239,4 +256,4 @@ Feature: Unattached status
1400 | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |
1401 | focal | yes | no | | yes | yes | yes | no | yes | usg |
1402 | impish | no | no | cis | no | no | no | no | no | |
1403- | jammy | no | no | cis | no | no | no | no | no | |
1404+ | jammy | no | no | cis | no | no | yes | no | no | |
1405diff --git a/help_data.yaml b/help_data.yaml
1406index d5d6486..c301573 100644
1407--- a/help_data.yaml
1408+++ b/help_data.yaml
1409@@ -61,6 +61,16 @@ livepatch:
1410 more about Ubuntu Kernel Livepatch service at
1411 https://ubuntu.com/security/livepatch
1412
1413+realtime-kernel:
1414+ help: |
1415+ The real-time kernel is a beta version of the 22.04 Ubuntu kernel with the
1416+ PREEMPT_RT patchset integrated for x86_64 and ARM64. It services extreme
1417+ latency-dependent use cases and provides deterministic response times to
1418+ service events. By meeting stringent preemption specifications, the
1419+ real-time kernel is suitable for telco applications and embedded devices
1420+ in industrial automation and robotics. To enroll in the beta program, visit
1421+ https://ubuntu.com/realtime-kernel
1422+
1423 ros:
1424 help: |
1425 ros provides access to a private PPA which includes security-related
1426diff --git a/keyrings/ubuntu-advantage-realtime-kernel.gpg b/keyrings/ubuntu-advantage-realtime-kernel.gpg
1427new file mode 100644
1428index 0000000..edf9246
1429Binary files /dev/null and b/keyrings/ubuntu-advantage-realtime-kernel.gpg differ
1430diff --git a/lib/upgrade_lts_contract.py b/lib/upgrade_lts_contract.py
1431index 2da9d58..ca7af18 100755
1432--- a/lib/upgrade_lts_contract.py
1433+++ b/lib/upgrade_lts_contract.py
1434@@ -34,21 +34,27 @@ version_to_codename = {
1435 "16.04": "xenial",
1436 "18.04": "bionic",
1437 "20.04": "focal",
1438- "20.10": "groovy",
1439+ "21.10": "impish",
1440+ "22.04": "jammy",
1441 }
1442
1443 current_codename_to_past_codename = {
1444 "xenial": "trusty",
1445 "bionic": "xenial",
1446 "focal": "bionic",
1447- "groovy": "focal",
1448+ "impish": "focal",
1449+ # We are considering the past release for Jammy to be Focal
1450+ # because we don't have any services available on Impish.
1451+ # Therefore, it is safer for us to try to process contract deltas
1452+ # using Focal
1453+ "jammy": "focal",
1454 }
1455
1456
1457 def process_contract_delta_after_apt_lock() -> None:
1458 logging.debug("Check whether to upgrade-lts-contract")
1459 if not UAConfig().is_attached:
1460- logging.debug("Skiping upgrade-lts-contract. Machine is unattached")
1461+ logging.debug("Skipping upgrade-lts-contract. Machine is unattached")
1462 return
1463 out, _err = subp(["lsof", "/var/lib/apt/lists/lock"], rcs=[0, 1])
1464 msg = "Starting upgrade-lts-contract."
1465@@ -58,7 +64,15 @@ def process_contract_delta_after_apt_lock() -> None:
1466 logging.debug(msg)
1467
1468 current_version = parse_os_release()["VERSION_ID"]
1469- current_release = version_to_codename[current_version]
1470+ current_release = version_to_codename.get(current_version)
1471+
1472+ if current_release is None:
1473+ msg = "Unable to get release codename for version: {}".format(
1474+ current_version
1475+ )
1476+ print(msg)
1477+ logging.warning(msg)
1478+ sys.exit(1)
1479
1480 if current_release == "trusty":
1481 msg = "Unable to execute upgrade-lts-contract.py on trusty"
1482@@ -66,7 +80,13 @@ def process_contract_delta_after_apt_lock() -> None:
1483 logging.warning(msg)
1484 sys.exit(1)
1485
1486- past_release = current_codename_to_past_codename[current_release]
1487+ past_release = current_codename_to_past_codename.get(current_release)
1488+ if past_release is None:
1489+ msg = "Could not find past release for: {}".format(current_release)
1490+ print(msg)
1491+ logging.warning(msg)
1492+ sys.exit(1)
1493+
1494 past_entitlements = UAConfig(series=past_release).entitlements
1495 new_entitlements = UAConfig(series=current_release).entitlements
1496
1497diff --git a/tools/refresh-keyrings.sh b/tools/refresh-keyrings.sh
1498index f4c1db1..850d8fb 100755
1499--- a/tools/refresh-keyrings.sh
1500+++ b/tools/refresh-keyrings.sh
1501@@ -28,6 +28,7 @@ ESM_APPS_KEY_ID="E8A443CE358113D187BEE0E6AB01A101DB53907B"
1502 FIPS_KEY_ID="E23341B2A1467EDBF07057D6C1997C40EDE22758"
1503 CIS_KEY_ID="81CF06E53F2C513A"
1504 ROS_KEY_ID="853874C8B0F10896"
1505+REALTIME_KEY_ID="F6D1E58F4DCD9F91"
1506
1507 generate_keyrings() {
1508 KEYRING_DIR="$1"
1509@@ -50,6 +51,8 @@ generate_keyrings() {
1510 service_name="cis";;
1511 $ROS_KEY_ID)
1512 service_name="ros";;
1513+ $REALTIME_KEY_ID)
1514+ service_name="realtime-kernel";;
1515 *)
1516 echo "Unhandled key id provided: " $key
1517 exit 1;
1518@@ -70,6 +73,6 @@ generate_keyrings() {
1519 }
1520
1521
1522-generate_keyrings $TARGET_DIR $EAL_KEY_ID $ESM_INFRA_KEY_ID $ESM_APPS_KEY_ID $FIPS_KEY_ID $CIS_KEY_ID $ROS_KEY_ID
1523+generate_keyrings $TARGET_DIR $EAL_KEY_ID $ESM_INFRA_KEY_ID $ESM_APPS_KEY_ID $FIPS_KEY_ID $CIS_KEY_ID $ROS_KEY_ID $REALTIME_KEY_ID
1524
1525 rm -rf $tmp_dir
1526diff --git a/tox.ini b/tox.ini
1527index 45962e0..dffbfa8 100644
1528--- a/tox.ini
1529+++ b/tox.ini
1530@@ -60,6 +60,7 @@ commands =
1531 behave-vm-16.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.xenial,series.all,series.lts" --tags="~upgrade"
1532 behave-vm-18.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.bionic,series.all,series.lts" --tags="~upgrade"
1533 behave-vm-20.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.focal,series.all,series.lts" --tags="~upgrade"
1534+ behave-vm-22.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.jammy,series.all,series.lts" --tags="~upgrade"
1535 behave-upgrade-16.04: behave -v {posargs} --tags="upgrade" --tags="series.xenial,series.all"
1536 behave-upgrade-18.04: behave -v {posargs} --tags="upgrade" --tags="series.bionic,series.all"
1537 behave-upgrade-20.04: behave -v {posargs} --tags="upgrade" --tags="series.focal,series.all"
1538@@ -72,6 +73,7 @@ commands =
1539 behave-awspro-20.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.focal,series.lts,series.all"
1540 behave-awspro-fips-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro.fips" --tags="series.xenial,series.lts,series.all"
1541 behave-awspro-fips-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro.fips" --tags="series.bionic,series.lts,series.all"
1542+ behave-awspro-fips-20.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro.fips" --tags="series.focal,series.lts,series.all"
1543 behave-azuregeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
1544 behave-azuregeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
1545 behave-azuregeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
1546@@ -80,6 +82,7 @@ commands =
1547 behave-azurepro-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.focal,series.lts,series.all"
1548 behave-azurepro-fips-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro.fips" --tags="series.xenial,series.lts,series.all"
1549 behave-azurepro-fips-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro.fips" --tags="series.bionic,series.lts,series.all"
1550+ behave-azurepro-fips-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro.fips" --tags="series.focal,series.lts,series.all"
1551 behave-gcpgeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
1552 behave-gcpgeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
1553 behave-gcpgeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
1554diff --git a/uaclient/clouds/identity.py b/uaclient/clouds/identity.py
1555index 7542d35..a39fef0 100644
1556--- a/uaclient/clouds/identity.py
1557+++ b/uaclient/clouds/identity.py
1558@@ -1,5 +1,6 @@
1559 import logging
1560 from enum import Enum
1561+from functools import lru_cache
1562 from typing import Dict, Optional, Tuple, Type # noqa: F401
1563
1564 from uaclient import clouds, exceptions, util
1565@@ -36,6 +37,7 @@ def get_instance_id() -> Optional[str]:
1566 return None
1567
1568
1569+@lru_cache(maxsize=None)
1570 @apply_config_settings_override("cloud_type")
1571 def get_cloud_type() -> Tuple[Optional[str], Optional[NoCloudTypeReason]]:
1572 if util.which("cloud-id"):
1573diff --git a/uaclient/clouds/tests/test_identity.py b/uaclient/clouds/tests/test_identity.py
1574index a21efee..ae2e4a6 100644
1575--- a/uaclient/clouds/tests/test_identity.py
1576+++ b/uaclient/clouds/tests/test_identity.py
1577@@ -40,7 +40,7 @@ class TestGetCloudType:
1578 @mock.patch(M_PATH + "util.subp", return_value=("somecloud\n", ""))
1579 def test_use_cloud_id_when_available(self, m_subp, m_which):
1580 """Use cloud-id utility to discover cloud type."""
1581- assert ("somecloud", None) == get_cloud_type()
1582+ assert ("somecloud", None) == get_cloud_type.__wrapped__()
1583 assert [mock.call("cloud-id")] == m_which.call_args_list
1584
1585 @mock.patch(M_PATH + "util.which", return_value="/usr/bin/cloud-id")
1586@@ -49,7 +49,10 @@ class TestGetCloudType:
1587 side_effect=exceptions.ProcessExecutionError("cloud-id"),
1588 )
1589 def test_error_when_cloud_id_fails(self, m_subp, m_which):
1590- assert (None, NoCloudTypeReason.CLOUD_ID_ERROR) == get_cloud_type()
1591+ assert (
1592+ None,
1593+ NoCloudTypeReason.CLOUD_ID_ERROR,
1594+ ) == get_cloud_type.__wrapped__()
1595
1596 @pytest.mark.parametrize(
1597 "settings_overrides",
1598@@ -80,7 +83,7 @@ class TestGetCloudType:
1599 expected_value = "test"
1600
1601 m_load_file.return_value = settings_overrides
1602- assert get_cloud_type() == (expected_value, None)
1603+ assert get_cloud_type.__wrapped__() == (expected_value, None)
1604
1605
1606 @mock.patch(M_PATH + "get_cloud_type")
1607diff --git a/uaclient/config.py b/uaclient/config.py
1608index 079708e..74bb06b 100644
1609--- a/uaclient/config.py
1610+++ b/uaclient/config.py
1611@@ -357,7 +357,7 @@ class UAConfig:
1612 entitlement_cfg["resourceToken"] = tokens_by_name[
1613 entitlement_name
1614 ]
1615- util.apply_series_overrides(entitlement_cfg, self.series)
1616+ util.apply_contract_overrides(entitlement_cfg, self.series)
1617 self._entitlements[entitlement_name] = entitlement_cfg
1618 return self._entitlements
1619
1620diff --git a/uaclient/contract.py b/uaclient/contract.py
1621index 47d0b5a..b47b484 100644
1622--- a/uaclient/contract.py
1623+++ b/uaclient/contract.py
1624@@ -347,7 +347,7 @@ def process_entitlement_delta(
1625 from uaclient.entitlements import entitlement_factory
1626
1627 if series_overrides:
1628- util.apply_series_overrides(new_access)
1629+ util.apply_contract_overrides(new_access)
1630
1631 deltas = util.get_dict_deltas(orig_access, new_access)
1632 ret = False
1633diff --git a/uaclient/entitlements/__init__.py b/uaclient/entitlements/__init__.py
1634index 1c6a156..8f7356d 100644
1635--- a/uaclient/entitlements/__init__.py
1636+++ b/uaclient/entitlements/__init__.py
1637@@ -7,6 +7,7 @@ from uaclient.entitlements.cc import CommonCriteriaEntitlement
1638 from uaclient.entitlements.cis import CISEntitlement
1639 from uaclient.entitlements.esm import ESMAppsEntitlement, ESMInfraEntitlement
1640 from uaclient.entitlements.livepatch import LivepatchEntitlement
1641+from uaclient.entitlements.realtime import RealtimeKernelEntitlement
1642 from uaclient.entitlements.ros import ROSEntitlement, ROSUpdatesEntitlement
1643 from uaclient.exceptions import EntitlementNotFoundError
1644 from uaclient.util import is_config_value_true
1645@@ -19,6 +20,7 @@ ENTITLEMENT_CLASSES = [
1646 fips.FIPSEntitlement,
1647 fips.FIPSUpdatesEntitlement,
1648 LivepatchEntitlement,
1649+ RealtimeKernelEntitlement,
1650 ROSEntitlement,
1651 ROSUpdatesEntitlement,
1652 ] # type: List[Type[UAEntitlement]]
1653diff --git a/uaclient/entitlements/base.py b/uaclient/entitlements/base.py
1654index bf1b926..4606b5c 100644
1655--- a/uaclient/entitlements/base.py
1656+++ b/uaclient/entitlements/base.py
1657@@ -209,7 +209,8 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1658 populated CanEnableFailure reason. This may expand to
1659 include other types of reasons in the future.
1660 """
1661- msg_ops = self.messaging.get("pre_enable", [])
1662+
1663+ msg_ops = self.messaging.get("pre_can_enable", [])
1664 if not util.handle_message_operations(msg_ops):
1665 return False, None
1666
1667@@ -237,6 +238,10 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1668 # every other reason means we can't continue
1669 return False, fail
1670
1671+ msg_ops = self.messaging.get("pre_enable", [])
1672+ if not util.handle_message_operations(msg_ops):
1673+ return False, None
1674+
1675 ret = self._perform_enable(silent=silent)
1676 if not ret:
1677 return False, None
1678@@ -449,7 +454,7 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1679 path_to_value="features.block_disable_on_enable",
1680 )
1681 for service in self.blocking_incompatible_services():
1682- ent = service.entitlement(self.cfg)
1683+ ent = service.entitlement(self.cfg, assume_yes=True)
1684
1685 user_msg = messages.INCOMPATIBLE_SERVICE.format(
1686 service_being_enabled=self.title,
1687@@ -474,7 +479,7 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1688 )
1689 event.info(disable_msg)
1690
1691- ret = ent.disable()
1692+ ret = ent.disable(silent=True)
1693 if not ret:
1694 return ret, None
1695
1696@@ -664,7 +669,7 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1697 event.info(info_msg=msg.msg, file_type=sys.stderr)
1698 return False, msg
1699
1700- ent = ent_cls(self.cfg)
1701+ ent = ent_cls(cfg=self.cfg, assume_yes=True)
1702
1703 is_service_enabled = (
1704 ent.application_status()[0] == status.ApplicationStatus.ENABLED
1705@@ -710,13 +715,16 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1706 """Check if system needs to be rebooted."""
1707 return util.should_reboot()
1708
1709- def _check_for_reboot_msg(self, operation: str) -> None:
1710+ def _check_for_reboot_msg(
1711+ self, operation: str, silent: bool = False
1712+ ) -> None:
1713 """Check if user should be alerted that a reboot must be performed.
1714
1715 @param operation: The operation being executed.
1716+ @param silent: Boolean set True to silence print/log of messages
1717 """
1718- if self._check_for_reboot():
1719- print(
1720+ if self._check_for_reboot() and not silent:
1721+ event.info(
1722 messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
1723 operation=operation
1724 )
1725@@ -763,7 +771,9 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1726 if not util.handle_message_operations(msg_ops):
1727 return False, None
1728
1729- self._check_for_reboot_msg(operation="disable operation")
1730+ self._check_for_reboot_msg(
1731+ operation="disable operation", silent=silent
1732+ )
1733 return True, None
1734
1735 def contract_status(self) -> ContractStatus:
1736@@ -836,7 +846,7 @@ class UAEntitlement(metaclass=abc.ABCMeta):
1737 transition_to_unentitled = bool(delta_entitlement == util.DROPPED_KEY)
1738 if not transition_to_unentitled:
1739 if delta_entitlement:
1740- util.apply_series_overrides(deltas)
1741+ util.apply_contract_overrides(deltas)
1742 delta_entitlement = deltas["entitlement"]
1743 if orig_access and "entitled" in delta_entitlement:
1744 transition_to_unentitled = delta_entitlement["entitled"] in (
1745diff --git a/uaclient/entitlements/cis.py b/uaclient/entitlements/cis.py
1746index 07a6a2e..b5c81f4 100644
1747--- a/uaclient/entitlements/cis.py
1748+++ b/uaclient/entitlements/cis.py
1749@@ -29,7 +29,7 @@ class CISEntitlement(repo.RepoEntitlement):
1750 ]
1751 } # type: MessagingOperationsDict
1752 if "usg" in self.valid_names:
1753- messages["pre_enable"] = [
1754+ messages["pre_can_enable"] = [
1755 "From Ubuntu 20.04 and onwards 'ua enable cis' has been",
1756 "replaced by 'ua enable usg'. See more information at:",
1757 USG_DOCS_URL,
1758diff --git a/uaclient/entitlements/fips.py b/uaclient/entitlements/fips.py
1759index 7c78543..b83b47e 100644
1760--- a/uaclient/entitlements/fips.py
1761+++ b/uaclient/entitlements/fips.py
1762@@ -87,6 +87,27 @@ class FIPSCommonEntitlement(repo.RepoEntitlement):
1763
1764 help_doc_url = "https://ubuntu.com/security/certifications#fips"
1765
1766+ fips_pro_package_holds = [
1767+ "fips-initramfs",
1768+ "libssl1.1",
1769+ "libssl1.1-hmac",
1770+ "libssl1.0.0",
1771+ "libssl1.0.0-hmac",
1772+ "libssl1.0.0",
1773+ "libssl1.0.0-hmac",
1774+ "linux-fips",
1775+ "openssh-client",
1776+ "openssh-client-hmac",
1777+ "openssh-server",
1778+ "openssh-server-hmac",
1779+ "openssl",
1780+ "strongswan",
1781+ "strongswan-hmac",
1782+ "libgcrypt20",
1783+ "libgcrypt20-hmac",
1784+ "fips-initramfs-generic",
1785+ ]
1786+
1787 @property
1788 def conditional_packages(self):
1789 """
1790@@ -155,19 +176,23 @@ class FIPSCommonEntitlement(repo.RepoEntitlement):
1791 )
1792 )
1793
1794- def _check_for_reboot_msg(self, operation: str) -> None:
1795+ def _check_for_reboot_msg(
1796+ self, operation: str, silent: bool = False
1797+ ) -> None:
1798 """Check if user should be alerted that a reboot must be performed.
1799
1800 @param operation: The operation being executed.
1801+ @param silent: Boolean set True to silence print/log of messages
1802 """
1803 reboot_required = util.should_reboot()
1804 event.needs_reboot(reboot_required)
1805 if reboot_required:
1806- event.info(
1807- messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
1808- operation=operation
1809+ if not silent:
1810+ event.info(
1811+ messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
1812+ operation=operation
1813+ )
1814 )
1815- )
1816 if operation == "install":
1817 self.cfg.add_notice(
1818 "", messages.FIPS_SYSTEM_REBOOT_REQUIRED.msg
1819@@ -361,6 +386,27 @@ class FIPSCommonEntitlement(repo.RepoEntitlement):
1820
1821 return False
1822
1823+ def setup_apt_config(self, silent: bool = False) -> None:
1824+ """Setup apt config based on the resourceToken and directives.
1825+
1826+ FIPS-specifically handle apt-mark unhold
1827+
1828+ :raise UserFacingError: on failure to setup any aspect of this apt
1829+ configuration
1830+ """
1831+ cmd = ["apt-mark", "showholds"]
1832+ holds = apt.run_apt_command(cmd, " ".join(cmd) + " failed.")
1833+ unholds = []
1834+ for hold in holds.splitlines():
1835+ if hold in self.fips_pro_package_holds:
1836+ unholds.append(hold)
1837+ if unholds:
1838+ unhold_cmd = ["apt-mark", "unhold"] + unholds
1839+ holds = apt.run_apt_command(
1840+ unhold_cmd, " ".join(unhold_cmd) + " failed."
1841+ )
1842+ super().setup_apt_config(silent=silent)
1843+
1844
1845 class FIPSEntitlement(FIPSCommonEntitlement):
1846
1847@@ -369,27 +415,10 @@ class FIPSEntitlement(FIPSCommonEntitlement):
1848 description = "NIST-certified core packages"
1849 origin = "UbuntuFIPS"
1850
1851- fips_pro_package_holds = [
1852- "fips-initramfs",
1853- "libssl1.1",
1854- "libssl1.1-hmac",
1855- "libssl1.0.0",
1856- "libssl1.0.0-hmac",
1857- "libssl1.0.0",
1858- "libssl1.0.0-hmac",
1859- "linux-fips",
1860- "openssh-client",
1861- "openssh-client-hmac",
1862- "openssh-server",
1863- "openssh-server-hmac",
1864- "openssl",
1865- "strongswan",
1866- "strongswan-hmac",
1867- ]
1868-
1869 @property
1870 def incompatible_services(self) -> Tuple[IncompatibleService, ...]:
1871 from uaclient.entitlements.livepatch import LivepatchEntitlement
1872+ from uaclient.entitlements.realtime import RealtimeKernelEntitlement
1873
1874 return (
1875 IncompatibleService(
1876@@ -398,6 +427,9 @@ class FIPSEntitlement(FIPSCommonEntitlement):
1877 IncompatibleService(
1878 FIPSUpdatesEntitlement, messages.FIPS_UPDATES_INVALIDATES_FIPS
1879 ),
1880+ IncompatibleService(
1881+ RealtimeKernelEntitlement, messages.REALTIME_FIPS_INCOMPATIBLE
1882+ ),
1883 )
1884
1885 @property
1886@@ -464,27 +496,6 @@ class FIPSEntitlement(FIPSCommonEntitlement):
1887 ],
1888 }
1889
1890- def setup_apt_config(self, silent: bool = False) -> None:
1891- """Setup apt config based on the resourceToken and directives.
1892-
1893- FIPS-specifically handle apt-mark unhold
1894-
1895- :raise UserFacingError: on failure to setup any aspect of this apt
1896- configuration
1897- """
1898- cmd = ["apt-mark", "showholds"]
1899- holds = apt.run_apt_command(cmd, " ".join(cmd) + " failed.")
1900- unholds = []
1901- for hold in holds.splitlines():
1902- if hold in self.fips_pro_package_holds:
1903- unholds.append(hold)
1904- if unholds:
1905- unhold_cmd = ["apt-mark", "unhold"] + unholds
1906- holds = apt.run_apt_command(
1907- unhold_cmd, " ".join(unhold_cmd) + " failed."
1908- )
1909- super().setup_apt_config(silent=silent)
1910-
1911 def _perform_enable(self, silent: bool = False) -> bool:
1912 cloud_type, error = get_cloud_type()
1913 if cloud_type is None and error == NoCloudTypeReason.CLOUD_ID_ERROR:
1914@@ -507,6 +518,20 @@ class FIPSUpdatesEntitlement(FIPSCommonEntitlement):
1915 description = "NIST-certified core packages with priority security updates"
1916
1917 @property
1918+ def incompatible_services(self) -> Tuple[IncompatibleService, ...]:
1919+ from uaclient.entitlements.realtime import RealtimeKernelEntitlement
1920+
1921+ return (
1922+ IncompatibleService(
1923+ FIPSEntitlement, messages.FIPS_INVALIDATES_FIPS_UPDATES
1924+ ),
1925+ IncompatibleService(
1926+ RealtimeKernelEntitlement,
1927+ messages.REALTIME_FIPS_UPDATES_INCOMPATIBLE,
1928+ ),
1929+ )
1930+
1931+ @property
1932 def messaging(self,) -> MessagingOperationsDict:
1933 post_enable = None # type: Optional[MessagingOperations]
1934 if util.is_container():
1935diff --git a/uaclient/entitlements/livepatch.py b/uaclient/entitlements/livepatch.py
1936index f652eb0..4fc9127 100644
1937--- a/uaclient/entitlements/livepatch.py
1938+++ b/uaclient/entitlements/livepatch.py
1939@@ -109,11 +109,16 @@ class LivepatchEntitlement(UAEntitlement):
1940 @property
1941 def incompatible_services(self) -> Tuple[IncompatibleService, ...]:
1942 from uaclient.entitlements.fips import FIPSEntitlement
1943+ from uaclient.entitlements.realtime import RealtimeKernelEntitlement
1944
1945 return (
1946 IncompatibleService(
1947 FIPSEntitlement, messages.LIVEPATCH_INVALIDATES_FIPS
1948 ),
1949+ IncompatibleService(
1950+ RealtimeKernelEntitlement,
1951+ messages.REALTIME_LIVEPATCH_INCOMPATIBLE,
1952+ ),
1953 )
1954
1955 @property
1956diff --git a/uaclient/entitlements/realtime.py b/uaclient/entitlements/realtime.py
1957new file mode 100644
1958index 0000000..35e5561
1959--- /dev/null
1960+++ b/uaclient/entitlements/realtime.py
1961@@ -0,0 +1,84 @@
1962+from typing import Tuple
1963+
1964+from uaclient import event_logger, messages, util
1965+from uaclient.entitlements import repo
1966+from uaclient.entitlements.base import IncompatibleService
1967+from uaclient.types import MessagingOperationsDict, StaticAffordance
1968+
1969+event = event_logger.get_event_logger()
1970+
1971+REALTIME_KERNEL_DOCS_URL = "https://ubuntu.com/realtime-kernel"
1972+
1973+
1974+class RealtimeKernelEntitlement(repo.RepoEntitlement):
1975+ name = "realtime-kernel"
1976+ title = "Real-Time Kernel"
1977+ description = "Beta-version Ubuntu Kernel with PREEMPT_RT patches"
1978+ help_doc_url = REALTIME_KERNEL_DOCS_URL
1979+ repo_key_file = "ubuntu-advantage-realtime-kernel.gpg"
1980+ is_beta = True
1981+ apt_noninteractive = True
1982+
1983+ def _check_for_reboot(self) -> bool:
1984+ """Check if system needs to be rebooted."""
1985+ reboot_required = util.should_reboot(
1986+ installed_pkgs=set(self.packages),
1987+ installed_pkgs_regex=set(["linux-.*-realtime"]),
1988+ )
1989+ event.needs_reboot(reboot_required)
1990+ return reboot_required
1991+
1992+ @property
1993+ def incompatible_services(self) -> Tuple[IncompatibleService, ...]:
1994+ from uaclient.entitlements.fips import (
1995+ FIPSEntitlement,
1996+ FIPSUpdatesEntitlement,
1997+ )
1998+ from uaclient.entitlements.livepatch import LivepatchEntitlement
1999+
2000+ return (
2001+ IncompatibleService(
2002+ FIPSEntitlement, messages.REALTIME_FIPS_INCOMPATIBLE
2003+ ),
2004+ IncompatibleService(
2005+ FIPSUpdatesEntitlement,
2006+ messages.REALTIME_FIPS_UPDATES_INCOMPATIBLE,
2007+ ),
2008+ IncompatibleService(
2009+ LivepatchEntitlement, messages.REALTIME_LIVEPATCH_INCOMPATIBLE
2010+ ),
2011+ )
2012+
2013+ @property
2014+ def static_affordances(self) -> Tuple[StaticAffordance, ...]:
2015+ return (
2016+ (
2017+ messages.REALTIME_ERROR_INSTALL_ON_CONTAINER,
2018+ lambda: util.is_container(),
2019+ False,
2020+ ),
2021+ )
2022+
2023+ @property
2024+ def messaging(self,) -> MessagingOperationsDict:
2025+ return {
2026+ "pre_enable": [
2027+ (
2028+ util.prompt_for_confirmation,
2029+ {
2030+ "msg": messages.REALTIME_BETA_PROMPT,
2031+ "assume_yes": self.assume_yes,
2032+ "default": True,
2033+ },
2034+ )
2035+ ],
2036+ "pre_disable": [
2037+ (
2038+ util.prompt_for_confirmation,
2039+ {
2040+ "msg": messages.REALTIME_PRE_DISABLE_PROMPT,
2041+ "assume_yes": self.assume_yes,
2042+ },
2043+ )
2044+ ],
2045+ }
2046diff --git a/uaclient/entitlements/tests/test_cc.py b/uaclient/entitlements/tests/test_cc.py
2047index 2e42b5f..362f5ea 100644
2048--- a/uaclient/entitlements/tests/test_cc.py
2049+++ b/uaclient/entitlements/tests/test_cc.py
2050@@ -110,8 +110,10 @@ class TestCommonCriteriaEntitlementEnable:
2051 @mock.patch("uaclient.util.should_reboot")
2052 @mock.patch("uaclient.util.subp")
2053 @mock.patch("uaclient.util.get_platform_info")
2054+ @mock.patch("uaclient.util.apply_contract_overrides")
2055 def test_enable_configures_apt_sources_and_auth_files(
2056 self,
2057+ _m_contract_overrides,
2058 m_platform_info,
2059 m_subp,
2060 m_should_reboot,
2061diff --git a/uaclient/entitlements/tests/test_fips.py b/uaclient/entitlements/tests/test_fips.py
2062index 8cf2898..51dc72f 100644
2063--- a/uaclient/entitlements/tests/test_fips.py
2064+++ b/uaclient/entitlements/tests/test_fips.py
2065@@ -21,6 +21,7 @@ from uaclient.entitlements.fips import (
2066 FIPSEntitlement,
2067 FIPSUpdatesEntitlement,
2068 )
2069+from uaclient.status import CanEnableFailureReason
2070
2071 M_PATH = "uaclient.entitlements.fips."
2072 M_LIVEPATCH_PATH = "uaclient.entitlements.livepatch.LivepatchEntitlement."
2073@@ -302,9 +303,11 @@ class TestFIPSEntitlementEnable:
2074 stack.enter_context(
2075 mock.patch.object(type(entitlement), "packages", m_packages)
2076 )
2077+ stack.enter_context(
2078+ mock.patch("uaclient.util.is_container", return_value=False)
2079+ )
2080
2081 m_can_enable.return_value = (True, None)
2082-
2083 assert (True, None) == entitlement.enable()
2084
2085 repo_url = "http://{}".format(entitlement.name.upper())
2086@@ -364,27 +367,20 @@ class TestFIPSEntitlementEnable:
2087 )
2088 )
2089
2090- if isinstance(entitlement, FIPSEntitlement):
2091- subp_calls = [
2092- mock.call(
2093- ["apt-mark", "showholds"],
2094- capture=True,
2095- retry_sleeps=apt.APT_RETRIES,
2096- env={},
2097- )
2098- ]
2099- else:
2100- subp_calls = []
2101- subp_calls.extend(
2102- [
2103- mock.call(
2104- ["apt-get", "update"],
2105- capture=True,
2106- retry_sleeps=apt.APT_RETRIES,
2107- env={},
2108- )
2109- ]
2110- )
2111+ subp_calls = [
2112+ mock.call(
2113+ ["apt-mark", "showholds"],
2114+ capture=True,
2115+ retry_sleeps=apt.APT_RETRIES,
2116+ env={},
2117+ ),
2118+ mock.call(
2119+ ["apt-get", "update"],
2120+ capture=True,
2121+ retry_sleeps=apt.APT_RETRIES,
2122+ env={},
2123+ ),
2124+ ]
2125 subp_calls += install_cmd
2126
2127 assert [mock.call()] == m_can_enable.call_args_list
2128@@ -1092,6 +1088,10 @@ class TestFipsSetupAPTConfig:
2129 "openssh-server\nlibssl1.1-hmac\nasdf\n",
2130 ["openssh-server", "libssl1.1-hmac"],
2131 ),
2132+ (
2133+ "libgcrypt20\nlibgcrypt20-hmac\nwow\n",
2134+ ["libgcrypt20", "libgcrypt20-hmac"],
2135+ ),
2136 ),
2137 )
2138 @mock.patch(M_REPOPATH + "RepoEntitlement.setup_apt_config")
2139@@ -1107,19 +1107,12 @@ class TestFipsSetupAPTConfig:
2140 """Unmark only fips-specific package holds if present."""
2141 run_apt_command.return_value = held_packages
2142 entitlement.setup_apt_config(silent=False)
2143- if isinstance(entitlement, FIPSUpdatesEntitlement):
2144- expected_calls = []
2145- else:
2146- expected_calls = [
2147- mock.call(
2148- ["apt-mark", "showholds"], "apt-mark showholds failed."
2149- )
2150- ]
2151- if unhold_packages:
2152- cmd = ["apt-mark", "unhold"] + unhold_packages
2153- expected_calls.append(
2154- mock.call(cmd, " ".join(cmd) + " failed.")
2155- )
2156+ expected_calls = [
2157+ mock.call(["apt-mark", "showholds"], "apt-mark showholds failed.")
2158+ ]
2159+ if unhold_packages:
2160+ cmd = ["apt-mark", "unhold"] + unhold_packages
2161+ expected_calls.append(mock.call(cmd, " ".join(cmd) + " failed."))
2162 assert expected_calls == run_apt_command.call_args_list
2163 assert [mock.call(silent=False)] == setup_apt_config.call_args_list
2164
2165@@ -1287,3 +1280,35 @@ class TestFIPSUpdatesEntitlementEnable:
2166 else:
2167 assert not m_read_cache.call_count
2168 assert not m_write_cache.call_count
2169+
2170+
2171+class TestFIPSUpdatesEntitlementCanEnable:
2172+ @mock.patch("uaclient.util.is_config_value_true", return_value=False)
2173+ def test_can_enable_false_if_fips_enabled(
2174+ self, m_is_config_value_true, capsys, entitlement_factory
2175+ ):
2176+ """When entitlement is disabled, can_enable returns True."""
2177+ entitlement = entitlement_factory(FIPSUpdatesEntitlement)
2178+ with mock.patch.object(
2179+ entitlement,
2180+ "applicability_status",
2181+ return_value=(status.ApplicabilityStatus.APPLICABLE, ""),
2182+ ):
2183+ with mock.patch.object(
2184+ entitlement,
2185+ "application_status",
2186+ return_value=(status.ApplicationStatus.DISABLED, ""),
2187+ ):
2188+ with mock.patch(
2189+ M_PATH + "FIPSEntitlement.application_status"
2190+ ) as m_fips_status:
2191+ m_fips_status.return_value = (
2192+ status.ApplicationStatus.ENABLED,
2193+ None,
2194+ )
2195+ actual_ret, reason = entitlement.can_enable()
2196+ assert actual_ret is False
2197+ assert (
2198+ reason.reason
2199+ == CanEnableFailureReason.INCOMPATIBLE_SERVICE
2200+ )
2201diff --git a/uaclient/entitlements/tests/test_livepatch.py b/uaclient/entitlements/tests/test_livepatch.py
2202index 191e91d..50fb50d 100644
2203--- a/uaclient/entitlements/tests/test_livepatch.py
2204+++ b/uaclient/entitlements/tests/test_livepatch.py
2205@@ -617,6 +617,7 @@ class TestLivepatchEntitlementEnable:
2206 @pytest.mark.parametrize("apt_update_success", (True, False))
2207 @mock.patch("uaclient.util.get_platform_info")
2208 @mock.patch("uaclient.util.subp")
2209+ @mock.patch("uaclient.util.apply_contract_overrides")
2210 @mock.patch("uaclient.apt.run_apt_install_command")
2211 @mock.patch("uaclient.apt.run_apt_update_command")
2212 @mock.patch("uaclient.util.which", return_value=False)
2213@@ -631,6 +632,7 @@ class TestLivepatchEntitlementEnable:
2214 m_which,
2215 m_run_apt_update,
2216 m_run_apt_install,
2217+ _m_contract_overrides,
2218 m_subp,
2219 _m_get_platform_info,
2220 m_livepatch_proxy,
2221@@ -678,6 +680,7 @@ class TestLivepatchEntitlementEnable:
2222
2223 @mock.patch("uaclient.util.get_platform_info")
2224 @mock.patch("uaclient.util.subp", return_value=("snapd", ""))
2225+ @mock.patch("uaclient.util.apply_contract_overrides")
2226 @mock.patch(
2227 "uaclient.util.which", side_effect=lambda cmd: cmd == "/usr/bin/snap"
2228 )
2229@@ -690,6 +693,7 @@ class TestLivepatchEntitlementEnable:
2230 m_can_enable,
2231 m_app_status,
2232 m_which,
2233+ _m_contract_overrides,
2234 m_subp,
2235 _m_get_platform_info,
2236 m_livepatch_proxy,
2237@@ -759,6 +763,7 @@ class TestLivepatchEntitlementEnable:
2238 @mock.patch("uaclient.apt.get_installed_packages", return_value=["snapd"])
2239 @mock.patch("uaclient.util.get_platform_info")
2240 @mock.patch("uaclient.util.subp")
2241+ @mock.patch("uaclient.util.apply_contract_overrides")
2242 @mock.patch("uaclient.util.which", side_effect=[True, True])
2243 @mock.patch(M_PATH + "LivepatchEntitlement.application_status")
2244 @mock.patch(
2245@@ -769,6 +774,7 @@ class TestLivepatchEntitlementEnable:
2246 m_can_enable,
2247 m_app_status,
2248 m_which,
2249+ _m_contract_overrides,
2250 m_subp,
2251 _m_get_platform_info,
2252 _m_get_installed_packages,
2253@@ -808,6 +814,7 @@ class TestLivepatchEntitlementEnable:
2254 @mock.patch("uaclient.apt.get_installed_packages", return_value=["snapd"])
2255 @mock.patch("uaclient.util.get_platform_info")
2256 @mock.patch("uaclient.util.subp")
2257+ @mock.patch("uaclient.util.apply_contract_overrides")
2258 @mock.patch("uaclient.util.which", side_effect=[True, True])
2259 @mock.patch(M_PATH + "LivepatchEntitlement.application_status")
2260 @mock.patch(
2261@@ -818,6 +825,7 @@ class TestLivepatchEntitlementEnable:
2262 m_can_enable,
2263 m_app_status,
2264 m_which,
2265+ _m_contract_overrides,
2266 m_subp,
2267 _m_get_platform_info,
2268 _m_get_installed_packages,
2269diff --git a/uaclient/entitlements/tests/test_repo.py b/uaclient/entitlements/tests/test_repo.py
2270index 084bab0..c59cb47 100644
2271--- a/uaclient/entitlements/tests/test_repo.py
2272+++ b/uaclient/entitlements/tests/test_repo.py
2273@@ -385,23 +385,25 @@ class TestProcessContractDeltas:
2274
2275 class TestRepoEnable:
2276 @pytest.mark.parametrize(
2277- "pre_enable_msg, output, can_enable_call_count",
2278+ "pre_enable_msg, output, perform_enable_call_count",
2279 (
2280 (["msg1", (lambda: False, {}), "msg2"], "msg1\n", 0),
2281 (["msg1", (lambda: True, {}), "msg2"], "msg1\nmsg2\n", 1),
2282 ),
2283 )
2284 @mock.patch.object(
2285- RepoTestEntitlement,
2286- "can_enable",
2287- return_value=(False, status.CanEnableFailure(None)),
2288+ RepoTestEntitlement, "_perform_enable", return_value=False
2289+ )
2290+ @mock.patch.object(
2291+ RepoTestEntitlement, "can_enable", return_value=(True, None)
2292 )
2293 def test_enable_can_exit_on_pre_enable_messaging_hooks(
2294 self,
2295- m_can_enable,
2296+ _m_can_enable,
2297+ m_perform_enable,
2298 pre_enable_msg,
2299 output,
2300- can_enable_call_count,
2301+ perform_enable_call_count,
2302 entitlement,
2303 capsys,
2304 ):
2305@@ -414,7 +416,7 @@ class TestRepoEnable:
2306 entitlement.enable()
2307 stdout, _ = capsys.readouterr()
2308 assert output == stdout
2309- assert can_enable_call_count == m_can_enable.call_count
2310+ assert perform_enable_call_count == m_perform_enable.call_count
2311
2312 @pytest.mark.parametrize(
2313 "pre_disable_msg,post_disable_msg,output,retval",
2314@@ -734,8 +736,10 @@ class TestRemoveAptConfig:
2315 @mock.patch(M_PATH + "apt.remove_apt_list_files")
2316 @mock.patch(M_PATH + "apt.run_apt_command")
2317 @mock.patch(M_PATH + "util.get_platform_info")
2318+ @mock.patch(M_PATH + "util.apply_contract_overrides")
2319 def test_repo_pin_priority_int_removes_apt_preferences(
2320 self,
2321+ _m_contract_overrides,
2322 m_get_platform,
2323 _m_run_apt_command,
2324 _m_remove_apt_list_files,
2325@@ -862,8 +866,10 @@ class TestSetupAptConfig:
2326 @mock.patch("uaclient.apt.setup_apt_proxy")
2327 @mock.patch(M_PATH + "apt.add_auth_apt_repo")
2328 @mock.patch(M_PATH + "apt.run_apt_install_command")
2329+ @mock.patch(M_PATH + "util.apply_contract_overrides")
2330 def test_install_prerequisite_packages(
2331 self,
2332+ _m_contract_overrides,
2333 m_run_apt_install_command,
2334 m_add_auth_repo,
2335 _m_setup_apt_proxy,
2336@@ -908,8 +914,10 @@ class TestSetupAptConfig:
2337 @mock.patch(M_PATH + "apt.add_auth_apt_repo")
2338 @mock.patch(M_PATH + "apt.run_apt_command")
2339 @mock.patch(M_PATH + "util.get_platform_info")
2340+ @mock.patch(M_PATH + "util.apply_contract_overrides")
2341 def test_setup_with_repo_pin_priority_never_removes_apt_preferences_file(
2342 self,
2343+ _m_contract_overrides,
2344 m_get_platform_info,
2345 m_run_apt_command,
2346 m_add_auth_repo,
2347@@ -940,8 +948,10 @@ class TestSetupAptConfig:
2348 @mock.patch(M_PATH + "apt.run_apt_update_command")
2349 @mock.patch(M_PATH + "apt.add_ppa_pinning")
2350 @mock.patch(M_PATH + "util.get_platform_info")
2351+ @mock.patch(M_PATH + "util.apply_contract_overrides")
2352 def test_setup_with_repo_pin_priority_int_adds_a_pins_repo_apt_preference(
2353 self,
2354+ _m_apply_overrides,
2355 m_get_platform_info,
2356 m_add_ppa_pinning,
2357 m_run_apt_update_command,
2358diff --git a/uaclient/messages.py b/uaclient/messages.py
2359index b7640b8..e9f5d9c 100644
2360--- a/uaclient/messages.py
2361+++ b/uaclient/messages.py
2362@@ -22,6 +22,7 @@ class TxtColor:
2363 OKGREEN = "\033[92m"
2364 DISABLEGREY = "\033[37m"
2365 FAIL = "\033[91m"
2366+ BOLD = "\033[1m"
2367 ENDC = "\033[0m"
2368
2369
2370@@ -661,6 +662,12 @@ FIPS_UPDATES_INVALIDATES_FIPS = NamedMessage(
2371 " FIPS Updates installs security patches that aren't officially"
2372 " certified.",
2373 )
2374+FIPS_INVALIDATES_FIPS_UPDATES = NamedMessage(
2375+ "fips-invalidates-fips-updates",
2376+ "FIPS Updates cannot be enabled if FIPS is enabled."
2377+ " FIPS Updates installs security patches that aren't officially"
2378+ " certified.",
2379+)
2380 LIVEPATCH_INVALIDATES_FIPS = NamedMessage(
2381 "livepatch-invalidates-fips",
2382 "Livepatch cannot be enabled while running the official FIPS"
2383@@ -668,6 +675,45 @@ LIVEPATCH_INVALIDATES_FIPS = NamedMessage(
2384 " with additional bug fixes and security updates, you can use"
2385 " the FIPS Updates service with Livepatch.",
2386 )
2387+REALTIME_FIPS_INCOMPATIBLE = NamedMessage(
2388+ "realtime-fips-incompatible",
2389+ "Realtime and FIPS require different kernels, so you cannot enable"
2390+ " both at the same time.",
2391+)
2392+REALTIME_FIPS_UPDATES_INCOMPATIBLE = NamedMessage(
2393+ "realtime-fips-updates-incompatible",
2394+ "Realtime and FIPS Updates require different kernels, so you cannot enable"
2395+ " both at the same time.",
2396+)
2397+REALTIME_LIVEPATCH_INCOMPATIBLE = NamedMessage(
2398+ "realtime-livepatch-incompatible",
2399+ "Livepatch is not currently supported for the real-time kernel.",
2400+)
2401+REALTIME_BETA_FLAG_REQUIRED = NamedMessage(
2402+ "beta-flag-required",
2403+ "Use `ua enable realtime-kernel --beta` to acknowledge the real-time"
2404+ " kernel is currently in beta and comes with no support.",
2405+)
2406+REALTIME_BETA_PROMPT = """\
2407+The real-time kernel is a beta version of the 22.04 Ubuntu kernel with the
2408+PREEMPT_RT patchset integrated for x86_64 and ARM64.
2409+
2410+{bold}You will not be able to revert to your original kernel after enabling\
2411+ real-time.{end_bold}
2412+
2413+Do you want to continue? [ default = Yes ]: (Y/n) """.format(
2414+ bold=TxtColor.BOLD, end_bold=TxtColor.ENDC
2415+)
2416+REALTIME_PRE_DISABLE_PROMPT = """\
2417+This will disable the Real-Time Kernel entitlement but the Real-Time Kernel\
2418+ will remain installed.
2419+Are you sure? (y/N) """
2420+
2421+REALTIME_ERROR_INSTALL_ON_CONTAINER = NamedMessage(
2422+ "realtime-error-install-on-container",
2423+ "Cannot install Real-Time Kernel on a container.",
2424+)
2425+
2426
2427 LOG_CONNECTIVITY_ERROR_TMPL = CONNECTIVITY_ERROR.msg + " {error}"
2428 LOG_CONNECTIVITY_ERROR_WITH_URL_TMPL = (
2429diff --git a/uaclient/status.py b/uaclient/status.py
2430index 7dd5ba2..3db4b67 100644
2431--- a/uaclient/status.py
2432+++ b/uaclient/status.py
2433@@ -225,17 +225,17 @@ Open a browser to: {}/subscribe""".format(
2434 BASE_UA_URL
2435 )
2436
2437-STATUS_UNATTACHED_TMPL = "{name: <14}{available: <11}{description}"
2438+STATUS_UNATTACHED_TMPL = "{name: <17}{available: <11}{description}"
2439
2440 STATUS_SIMULATED_TMPL = """\
2441-{name: <14}{available: <11}{entitled: <11}{auto_enabled: <14}{description}"""
2442+{name: <17}{available: <11}{entitled: <11}{auto_enabled: <14}{description}"""
2443
2444-STATUS_HEADER = "SERVICE ENTITLED STATUS DESCRIPTION"
2445+STATUS_HEADER = "SERVICE ENTITLED STATUS DESCRIPTION"
2446 # The widths listed below for entitled and status are actually 9 characters
2447 # less than reality because we colorize the values in entitled and status
2448 # columns. Colorizing has an opening and closing set of unprintable characters
2449 # that factor into formats len() calculations
2450-STATUS_TMPL = "{name: <14}{entitled: <19}{status: <19}{description}"
2451+STATUS_TMPL = "{name: <17}{entitled: <19}{status: <19}{description}"
2452
2453
2454 def colorize(string: str) -> str:
2455diff --git a/uaclient/tests/test_cli_attach.py b/uaclient/tests/test_cli_attach.py
2456index 75cd0e1..e568f5f 100644
2457--- a/uaclient/tests/test_cli_attach.py
2458+++ b/uaclient/tests/test_cli_attach.py
2459@@ -532,14 +532,14 @@ class TestActionAttach:
2460 assert expected == json.loads(fake_stdout.getvalue())
2461
2462 @mock.patch("uaclient.contract.process_entitlement_delta")
2463- @mock.patch("uaclient.util.apply_series_overrides")
2464+ @mock.patch("uaclient.util.apply_contract_overrides")
2465 @mock.patch("uaclient.contract.UAContractClient.request_url")
2466 @mock.patch("uaclient.jobs.update_messaging.update_apt_and_motd_messages")
2467 def test_attach_when_one_service_fails_to_enable(
2468 self,
2469 _m_update_messages,
2470 m_request_url,
2471- _m_apply_series_overrides,
2472+ _m_apply_contract_overrides,
2473 m_process_entitlement_delta,
2474 _m_getuid,
2475 FakeConfig,
2476diff --git a/uaclient/tests/test_cli_collect_logs.py b/uaclient/tests/test_cli_collect_logs.py
2477index df6feba..accf283 100644
2478--- a/uaclient/tests/test_cli_collect_logs.py
2479+++ b/uaclient/tests/test_cli_collect_logs.py
2480@@ -124,8 +124,8 @@ class TestActionCollectLogs:
2481 ),
2482 ]
2483
2484- assert m_copy.call_count == 14
2485- assert redact.call_count == 14
2486+ assert m_copy.call_count == 15
2487+ assert redact.call_count == 15
2488
2489
2490 class TestParser:
2491diff --git a/uaclient/tests/test_cli_status.py b/uaclient/tests/test_cli_status.py
2492index 125a896..1331955 100644
2493--- a/uaclient/tests/test_cli_status.py
2494+++ b/uaclient/tests/test_cli_status.py
2495@@ -24,6 +24,7 @@ RESPONSE_AVAILABLE_SERVICES = [
2496 {"name": "esm-infra", "available": False},
2497 {"name": "esm-apps", "available": True},
2498 {"name": "fips-updates", "available": False},
2499+ {"name": "realtime-kernel", "available": False},
2500 {"name": "ros", "available": False},
2501 {"name": "ros-updates", "available": False},
2502 ]
2503@@ -68,41 +69,34 @@ RESPONSE_CONTRACT_INFO = {
2504 }
2505
2506 SIMULATED_STATUS = """\
2507-SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
2508-esm-infra no yes yes UA Infra: Extended Security\
2509- Maintenance (ESM)
2510-fips no no no NIST-certified core packages
2511-fips-updates no no no NIST-certified core packages\
2512- with priority security updates
2513-livepatch yes yes no Canonical Livepatch service
2514-"""
2515+SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
2516+esm-infra no yes yes UA Infra: Extended Security Maintenance (ESM)
2517+fips no no no NIST-certified core packages
2518+fips-updates no no no NIST-certified core packages with priority security updates
2519+livepatch yes yes no Canonical Livepatch service
2520+""" # noqa: E501
2521
2522 UNATTACHED_STATUS = """\
2523-SERVICE AVAILABLE DESCRIPTION
2524-esm-infra no UA Infra: Extended Security Maintenance (ESM)
2525-fips no NIST-certified core packages
2526-fips-updates no NIST-certified core packages with priority\
2527- security updates
2528-livepatch yes Canonical Livepatch service
2529+SERVICE AVAILABLE DESCRIPTION
2530+esm-infra no UA Infra: Extended Security Maintenance (ESM)
2531+fips no NIST-certified core packages
2532+fips-updates no NIST-certified core packages with priority security updates
2533+livepatch yes Canonical Livepatch service
2534
2535 This machine is not attached to a UA subscription.
2536 See https://ubuntu.com/advantage
2537-"""
2538+""" # noqa: E501
2539
2540 ATTACHED_STATUS = """\
2541-SERVICE ENTITLED STATUS DESCRIPTION
2542-esm-apps no {dash} UA Apps: Extended Security Maintenance\
2543- (ESM)
2544-esm-infra no {dash} UA Infra: Extended Security Maintenance\
2545- (ESM)
2546-fips no {dash} NIST-certified core packages
2547-fips-updates no {dash} NIST-certified core packages with\
2548- priority security updates
2549-livepatch no {dash} Canonical Livepatch service
2550-ros no {dash} Security Updates for the Robot\
2551- Operating System
2552-ros-updates no {dash} All Updates for the Robot Operating\
2553- System
2554+SERVICE ENTITLED STATUS DESCRIPTION
2555+esm-apps no {dash} UA Apps: Extended Security Maintenance (ESM)
2556+esm-infra no {dash} UA Infra: Extended Security Maintenance (ESM)
2557+fips no {dash} NIST-certified core packages
2558+fips-updates no {dash} NIST-certified core packages with priority security updates
2559+livepatch no {dash} Canonical Livepatch service
2560+realtime-kernel no {dash} Beta-version Ubuntu Kernel with PREEMPT_RT patches
2561+ros no {dash} Security Updates for the Robot Operating System
2562+ros-updates no {dash} All Updates for the Robot Operating System
2563 {notices}
2564 Enable services with: ua enable <service>
2565
2566@@ -110,17 +104,15 @@ Enable services with: ua enable <service>
2567 Subscription: test_contract
2568 Valid until: 2040-05-08 19:02:26+00:00
2569 Technical support level: n/a
2570-"""
2571+""" # noqa: E501
2572
2573 # Omit beta services from status
2574 ATTACHED_STATUS_NOBETA = """\
2575-SERVICE ENTITLED STATUS DESCRIPTION
2576-esm-infra no {dash} UA Infra: Extended Security Maintenance\
2577- (ESM)
2578-fips no {dash} NIST-certified core packages
2579-fips-updates no {dash} NIST-certified core packages with\
2580- priority security updates
2581-livepatch no {dash} Canonical Livepatch service
2582+SERVICE ENTITLED STATUS DESCRIPTION
2583+esm-infra no {dash} UA Infra: Extended Security Maintenance (ESM)
2584+fips no {dash} NIST-certified core packages
2585+fips-updates no {dash} NIST-certified core packages with priority security updates
2586+livepatch no {dash} Canonical Livepatch service
2587 {notices}
2588 Enable services with: ua enable <service>
2589
2590@@ -128,9 +120,9 @@ Enable services with: ua enable <service>
2591 Subscription: test_contract
2592 Valid until: 2040-05-08 19:02:26+00:00
2593 Technical support level: n/a
2594-"""
2595+""" # noqa: E501
2596
2597-BETA_SVC_NAMES = ["esm-apps", "ros", "ros-updates"]
2598+BETA_SVC_NAMES = ["esm-apps", "realtime-kernel", "ros", "ros-updates"]
2599
2600 SERVICES_JSON_ALL = [
2601 {
2602@@ -186,6 +178,16 @@ SERVICES_JSON_ALL = [
2603 "blocked_by": [],
2604 },
2605 {
2606+ "description": "TODO",
2607+ "description_override": None,
2608+ "entitled": "no",
2609+ "name": "realtime-kernel",
2610+ "status": "—",
2611+ "status_details": "",
2612+ "available": "yes",
2613+ "blocked_by": [],
2614+ },
2615+ {
2616 "description": "Security Updates for the Robot Operating System",
2617 "description_override": None,
2618 "entitled": "no",
2619@@ -739,6 +741,14 @@ class TestActionStatus:
2620 {
2621 "auto_enabled": "no",
2622 "available": "no",
2623+ "description": "Beta-version Ubuntu Kernel with PREEMPT_RT"
2624+ " patches",
2625+ "entitled": "no",
2626+ "name": "realtime-kernel",
2627+ },
2628+ {
2629+ "auto_enabled": "no",
2630+ "available": "no",
2631 "description": "Security Updates for the Robot Operating"
2632 " System",
2633 "entitled": "no",
2634@@ -754,7 +764,7 @@ class TestActionStatus:
2635 ]
2636
2637 if not use_all:
2638- expected_services = expected_services[1:-2]
2639+ expected_services = expected_services[1:-3]
2640
2641 expected = {
2642 "_doc": "Content provided in json response is currently considered"
2643diff --git a/uaclient/tests/test_upgrade_lts_contract.py b/uaclient/tests/test_upgrade_lts_contract.py
2644index 231fa17..7f76620 100644
2645--- a/uaclient/tests/test_upgrade_lts_contract.py
2646+++ b/uaclient/tests/test_upgrade_lts_contract.py
2647@@ -16,7 +16,7 @@ class TestUpgradeLTSContract:
2648 def test_unattached_noops(self, m_is_attached, capsys, caplog_text):
2649 expected_logs = [
2650 "Check whether to upgrade-lts-contract",
2651- "Skiping upgrade-lts-contract. Machine is unattached",
2652+ "Skipping upgrade-lts-contract. Machine is unattached",
2653 ]
2654
2655 process_contract_delta_after_apt_lock()
2656@@ -67,6 +67,72 @@ class TestUpgradeLTSContract:
2657 )
2658 @mock.patch("lib.upgrade_lts_contract.parse_os_release")
2659 @mock.patch("lib.upgrade_lts_contract.subp")
2660+ def test_upgrade_cancel_when_current_version_not_supported(
2661+ self, m_subp, m_parse_os, m_is_attached, capsys, caplog_text
2662+ ):
2663+ m_parse_os.return_value = {"VERSION_ID": "NOT-SUPPORTED"}
2664+ m_subp.return_value = ("", "")
2665+
2666+ expected_msgs = [
2667+ "Starting upgrade-lts-contract.",
2668+ "Unable to get release codename for version: NOT-SUPPORTED",
2669+ ]
2670+ expected_logs = ["Check whether to upgrade-lts-contract"]
2671+ with pytest.raises(SystemExit) as execinfo:
2672+ process_contract_delta_after_apt_lock()
2673+
2674+ assert 1 == execinfo.value.code
2675+ assert 1 == m_is_attached.call_count
2676+ assert 1 == m_parse_os.call_count
2677+ assert 1 == m_subp.call_count
2678+ out, _err = capsys.readouterr()
2679+ assert out == "\n".join(expected_msgs) + "\n"
2680+ debug_logs = caplog_text()
2681+ for log in expected_msgs + expected_logs:
2682+ assert log in debug_logs
2683+
2684+ @mock.patch(
2685+ "uaclient.config.UAConfig.is_attached",
2686+ new_callable=mock.PropertyMock,
2687+ return_value=True,
2688+ )
2689+ @mock.patch("lib.upgrade_lts_contract.parse_os_release")
2690+ @mock.patch("lib.upgrade_lts_contract.subp")
2691+ def test_upgrade_cancel_when_past_version_not_supported(
2692+ self, m_subp, m_parse_os, m_is_attached, capsys, caplog_text
2693+ ):
2694+ m_parse_os.return_value = {"VERSION_ID": "20.10"}
2695+ m_subp.return_value = ("", "")
2696+
2697+ expected_msgs = [
2698+ "Starting upgrade-lts-contract.",
2699+ "Could not find past release for: groovy",
2700+ ]
2701+ expected_logs = ["Check whether to upgrade-lts-contract"]
2702+ with pytest.raises(SystemExit) as execinfo:
2703+ with mock.patch(
2704+ "lib.upgrade_lts_contract.version_to_codename",
2705+ {"20.10": "groovy"},
2706+ ):
2707+ process_contract_delta_after_apt_lock()
2708+
2709+ assert 1 == execinfo.value.code
2710+ assert 1 == m_is_attached.call_count
2711+ assert 1 == m_parse_os.call_count
2712+ assert 1 == m_subp.call_count
2713+ out, _err = capsys.readouterr()
2714+ assert out == "\n".join(expected_msgs) + "\n"
2715+ debug_logs = caplog_text()
2716+ for log in expected_msgs + expected_logs:
2717+ assert log in debug_logs
2718+
2719+ @mock.patch(
2720+ "uaclient.config.UAConfig.is_attached",
2721+ new_callable=mock.PropertyMock,
2722+ return_value=True,
2723+ )
2724+ @mock.patch("lib.upgrade_lts_contract.parse_os_release")
2725+ @mock.patch("lib.upgrade_lts_contract.subp")
2726 @mock.patch("lib.upgrade_lts_contract.process_entitlements_delta")
2727 @mock.patch("lib.upgrade_lts_contract.time.sleep")
2728 def test_upgrade_contract_when_apt_lock_is_held(
2729diff --git a/uaclient/tests/test_util.py b/uaclient/tests/test_util.py
2730index 35bd8c0..99a7aa2 100644
2731--- a/uaclient/tests/test_util.py
2732+++ b/uaclient/tests/test_util.py
2733@@ -474,57 +474,244 @@ class TestGetPlatformInfo:
2734 assert expected == util.get_platform_info.__wrapped__()
2735
2736
2737-class TestApplySeriesOverrides:
2738+class TestApplyContractOverrides:
2739+ @pytest.mark.parametrize(
2740+ "override_selector,expected_weight",
2741+ (
2742+ ({"selector1": "valueX", "selector2": "valueZ"}, 0),
2743+ ({"selector1": "valueA", "selector2": "valueZ"}, 0),
2744+ ({"selector1": "valueX", "selector2": "valueB"}, 0),
2745+ ({"selector1": "valueA"}, 1),
2746+ ({"selector2": "valueB"}, 2),
2747+ ({"selector1": "valueA", "selector2": "valueB"}, 3),
2748+ ),
2749+ )
2750+ def test_get_override_weight(self, override_selector, expected_weight):
2751+ selector_values = {"selector1": "valueA", "selector2": "valueB"}
2752+ selector_weights = {"selector1": 1, "selector2": 2}
2753+ with mock.patch(
2754+ "uaclient.util.OVERRIDE_SELECTOR_WEIGHTS", selector_weights
2755+ ):
2756+ assert expected_weight == util._get_override_weight(
2757+ override_selector, selector_values
2758+ )
2759+
2760 def test_error_on_non_entitlement_dict(self):
2761 """Raise a runtime error when seeing invalid dict type."""
2762 with pytest.raises(RuntimeError) as exc:
2763- util.apply_series_overrides({"some": "dict"})
2764+ util.apply_contract_overrides({"some": "dict"})
2765 error = (
2766 'Expected entitlement access dict. Missing "entitlement" key:'
2767 " {'some': 'dict'}"
2768 )
2769 assert error == str(exc.value)
2770
2771+ @pytest.mark.parametrize("include_overrides", (True, False))
2772 @mock.patch(
2773- "uaclient.util.get_platform_info", return_value={"series": "xenial"}
2774+ "uaclient.util.get_platform_info", return_value={"series": "ubuntuX"}
2775 )
2776- def test_mutates_orig_access_dict(self, _):
2777- """Mutate orig_access dict when called."""
2778+ @mock.patch(
2779+ "uaclient.clouds.identity.get_cloud_type", return_value=(None, "")
2780+ )
2781+ def test_return_same_dict_when_no_overrides_match(
2782+ self, _m_cloud_type, _m_platform_info, include_overrides
2783+ ):
2784 orig_access = {
2785 "entitlement": {
2786- "a": {"a1": "av1", "a2": {"aa2": "aav2"}},
2787- "b": "b1",
2788- "c": "c1",
2789+ "affordances": {"some_affordance": ["ubuntuX"]},
2790+ "directives": {"some_directive": ["ubuntuX"]},
2791+ "obligations": {"some_obligation": False},
2792+ }
2793+ }
2794+ # exactly the same
2795+ expected = {
2796+ "entitlement": {
2797+ "affordances": {"some_affordance": ["ubuntuX"]},
2798+ "directives": {"some_directive": ["ubuntuX"]},
2799+ "obligations": {"some_obligation": False},
2800+ }
2801+ }
2802+ if include_overrides:
2803+ orig_access["entitlement"].update(
2804+ {
2805+ "series": {
2806+ "dontMatch": {
2807+ "affordances": {
2808+ "some_affordance": ["ubuntuX-series-overriden"]
2809+ }
2810+ }
2811+ },
2812+ "overrides": [
2813+ {
2814+ "selector": {"series": "dontMatch"},
2815+ "affordances": {
2816+ "some_affordance": ["ubuntuX-series-overriden"]
2817+ },
2818+ },
2819+ {
2820+ "selector": {"cloud": "dontMatch"},
2821+ "affordances": {
2822+ "some_affordance": ["ubuntuX-cloud-overriden"]
2823+ },
2824+ },
2825+ ],
2826+ }
2827+ )
2828+
2829+ util.apply_contract_overrides(orig_access)
2830+ assert expected == orig_access
2831+
2832+ @mock.patch(
2833+ "uaclient.util.get_platform_info", return_value={"series": "ubuntuX"}
2834+ )
2835+ def test_missing_keys_are_included(self, _m_platform_info):
2836+ orig_access = {
2837+ "entitlement": {
2838+ "series": {"ubuntuX": {"directives": {"suites": ["ubuntuX"]}}}
2839+ }
2840+ }
2841+ expected = {"entitlement": {"directives": {"suites": ["ubuntuX"]}}}
2842+
2843+ util.apply_contract_overrides(orig_access)
2844+
2845+ assert expected == orig_access
2846+
2847+ @pytest.mark.parametrize(
2848+ "series_selector,cloud_selector,series_cloud_selector,expected_value",
2849+ (
2850+ # apply_overrides_when_only_series_match
2851+ ("no-match", "no-match", "no-match", "old_series_overriden"),
2852+ # series selector is applied over old series override
2853+ ("ubuntuX", "no-match", "no-match", "series_overriden"),
2854+ # cloud selector is applied over series override
2855+ ("no-match", "cloudX", "no-match", "cloud_overriden"),
2856+ # cloud selector is applied over series selector
2857+ ("ubuntuX", "cloudX", "no-match", "cloud_overriden"),
2858+ # cloud and series together are applied over others
2859+ ("ubuntuX", "cloudX", "cloudX", "both_overriden"),
2860+ ),
2861+ )
2862+ @mock.patch(
2863+ "uaclient.util.get_platform_info", return_value={"series": "ubuntuX"}
2864+ )
2865+ @mock.patch(
2866+ "uaclient.clouds.identity.get_cloud_type",
2867+ return_value=("cloudX", None),
2868+ )
2869+ def test_applies_contract_overrides_respecting_weight(
2870+ self,
2871+ _m_cloud_type,
2872+ _m_platform_info,
2873+ series_selector,
2874+ cloud_selector,
2875+ series_cloud_selector,
2876+ expected_value,
2877+ ):
2878+ """Apply the expected overrides to orig_access dict when called."""
2879+ orig_access = {
2880+ "entitlement": {
2881+ "affordances": {"some_affordance": ["original_affordance"]},
2882 "series": {
2883- "trusty": {"a": "t1"},
2884- "xenial": {"a": {"a2": {"aa2": "xxv2"}}, "b": "bx1"},
2885+ "ubuntuX": {
2886+ "affordances": {
2887+ "some_affordance": ["old_series_overriden"]
2888+ }
2889+ }
2890 },
2891+ "overrides": [
2892+ {
2893+ "selector": {"series": series_selector},
2894+ "affordances": {
2895+ "some_affordance": ["series_overriden"]
2896+ },
2897+ },
2898+ {
2899+ "selector": {"cloud": cloud_selector},
2900+ "affordances": {
2901+ "some_affordance": ["cloud_overriden"]
2902+ },
2903+ },
2904+ {
2905+ "selector": {
2906+ "series": series_selector,
2907+ "cloud": series_cloud_selector,
2908+ },
2909+ "affordances": {"some_affordance": ["both_overriden"]},
2910+ },
2911+ ],
2912 }
2913 }
2914+
2915 expected = {
2916 "entitlement": {
2917- "a": {"a1": "av1", "a2": {"aa2": "xxv2"}},
2918- "b": "bx1",
2919- "c": "c1",
2920+ "affordances": {"some_affordance": [expected_value]}
2921 }
2922 }
2923- util.apply_series_overrides(orig_access)
2924+
2925+ util.apply_contract_overrides(orig_access)
2926 assert orig_access == expected
2927
2928 @mock.patch(
2929- "uaclient.util.get_platform_info", return_value={"series": "xenial"}
2930+ "uaclient.util.get_platform_info", return_value={"series": "ubuntuX"}
2931+ )
2932+ @mock.patch(
2933+ "uaclient.clouds.identity.get_cloud_type",
2934+ return_value=("cloudX", None),
2935 )
2936- def test_missing_keys_are_handled(self, _):
2937+ def test_different_overrides_applied_together(
2938+ self, _m_cloud_type, _m_platform_info
2939+ ):
2940+ """Apply different overrides from different matching selectors."""
2941 orig_access = {
2942 "entitlement": {
2943- "series": {"xenial": {"directives": {"suites": ["xenial"]}}}
2944+ "affordances": {"some_affordance": ["original_affordance"]},
2945+ "directives": {"some_directive": ["original_directive"]},
2946+ "obligations": {"some_obligation": False},
2947+ "series": {
2948+ "ubuntuX": {
2949+ "affordances": {
2950+ "new_affordance": ["new_affordance_value"]
2951+ }
2952+ }
2953+ },
2954+ "overrides": [
2955+ {
2956+ "selector": {"series": "ubuntuX"},
2957+ "affordances": {
2958+ "some_affordance": ["series_overriden"]
2959+ },
2960+ },
2961+ {
2962+ "selector": {"cloud": "cloudX"},
2963+ "directives": {"some_directive": ["cloud_overriden"]},
2964+ },
2965+ {
2966+ "selector": {"series": "ubuntuX", "cloud": "cloudX"},
2967+ "obligations": {
2968+ "new_obligation": True,
2969+ "some_obligation": True,
2970+ },
2971+ },
2972+ ],
2973 }
2974 }
2975- expected = {"entitlement": {"directives": {"suites": ["xenial"]}}}
2976
2977- util.apply_series_overrides(orig_access)
2978+ expected = {
2979+ "entitlement": {
2980+ "affordances": {
2981+ "new_affordance": ["new_affordance_value"],
2982+ "some_affordance": ["series_overriden"],
2983+ },
2984+ "directives": {"some_directive": ["cloud_overriden"]},
2985+ "obligations": {
2986+ "new_obligation": True,
2987+ "some_obligation": True,
2988+ },
2989+ }
2990+ }
2991
2992- assert expected == orig_access
2993+ util.apply_contract_overrides(orig_access)
2994+ assert orig_access == expected
2995
2996
2997 class TestGetMachineId:
2998@@ -1263,13 +1450,18 @@ class TestShouldReboot:
2999 assert 1 == m_load_file.call_count
3000
3001 @pytest.mark.parametrize(
3002- "installed_pkgs,reboot_required_pkgs,expected_ret",
3003+ "installed_pkgs,installed_pkgs_regex,reboot_required_pkgs,"
3004+ "expected_ret",
3005 (
3006- (set(["a", "b", "c"]), "", False),
3007- (set(["a", "b", "c"]), "a", True),
3008- (set(["a", "b", "c"]), "a\ne", True),
3009- (set(["a", "b", "c"]), "d\ne", False),
3010- (None, "a\ne", True),
3011+ (set(["a", "b", "c"]), None, "", False),
3012+ (set(["a", "b", "c"]), None, "a", True),
3013+ (set(["a", "b", "c"]), None, "a\ne", True),
3014+ (set(["a", "b", "c"]), None, "d\ne", False),
3015+ (set(["a", "b", "c"]), set(["t.."]), "a\ne", True),
3016+ (set(["a", "b", "c"]), set(["t.."]), "one\ntwo", True),
3017+ (None, set(["^t..$"]), "one\ntwo", True),
3018+ (None, set(["^t..$"]), "one\nthree", False),
3019+ (None, None, "a\ne", True),
3020 ),
3021 )
3022 @mock.patch("os.path.exists")
3023@@ -1279,11 +1471,13 @@ class TestShouldReboot:
3024 m_load_file,
3025 m_path,
3026 installed_pkgs,
3027+ installed_pkgs_regex,
3028 reboot_required_pkgs,
3029 expected_ret,
3030 ):
3031 m_path.return_value = True
3032 m_load_file.return_value = reboot_required_pkgs
3033 assert expected_ret == util.should_reboot(
3034- installed_pkgs=installed_pkgs
3035+ installed_pkgs=installed_pkgs,
3036+ installed_pkgs_regex=installed_pkgs_regex,
3037 )
3038diff --git a/uaclient/util.py b/uaclient/util.py
3039index 674f302..346f202 100644
3040--- a/uaclient/util.py
3041+++ b/uaclient/util.py
3042@@ -44,6 +44,8 @@ PROXY_VALIDATION_APT_HTTPS_URL = "https://esm.ubuntu.com"
3043 PROXY_VALIDATION_SNAP_HTTP_URL = "http://api.snapcraft.io"
3044 PROXY_VALIDATION_SNAP_HTTPS_URL = "https://api.snapcraft.io"
3045
3046+OVERRIDE_SELECTOR_WEIGHTS = {"series_overrides": 1, "series": 2, "cloud": 3}
3047+
3048 event = event_logger.get_event_logger()
3049
3050
3051@@ -100,7 +102,43 @@ class DatetimeAwareJSONDecoder(json.JSONDecoder):
3052 return o
3053
3054
3055-def apply_series_overrides(
3056+def _get_override_weight(
3057+ override_selector: Dict[str, str], selector_values: Dict[str, str]
3058+) -> int:
3059+ override_weight = 0
3060+ for selector, value in override_selector.items():
3061+ if (selector, value) not in selector_values.items():
3062+ return 0
3063+ override_weight += OVERRIDE_SELECTOR_WEIGHTS[selector]
3064+
3065+ return override_weight
3066+
3067+
3068+def _select_overrides(
3069+ entitlement: Dict[str, Any], series_name: str, cloud_type: str
3070+) -> Dict[int, Dict[str, Any]]:
3071+ overrides = {}
3072+
3073+ selector_values = {"series": series_name, "cloud": cloud_type}
3074+
3075+ series_overrides = entitlement.pop("series", {}).pop(series_name, {})
3076+ if series_overrides:
3077+ overrides[
3078+ OVERRIDE_SELECTOR_WEIGHTS["series_overrides"]
3079+ ] = series_overrides
3080+
3081+ general_overrides = entitlement.pop("overrides", [])
3082+ for override in general_overrides:
3083+ weight = _get_override_weight(
3084+ override.pop("selector"), selector_values
3085+ )
3086+ if weight:
3087+ overrides[weight] = override
3088+
3089+ return overrides
3090+
3091+
3092+def apply_contract_overrides(
3093 orig_access: Dict[str, Any], series: str = None
3094 ) -> None:
3095 """Apply series-specific overrides to an entitlement dict.
3096@@ -117,23 +155,30 @@ def apply_series_overrides(
3097
3098 :param orig_access: Dict with original entitlement access details
3099 """
3100+ from uaclient.clouds.identity import get_cloud_type
3101+
3102 if not all([isinstance(orig_access, dict), "entitlement" in orig_access]):
3103 raise RuntimeError(
3104 'Expected entitlement access dict. Missing "entitlement" key:'
3105 " {}".format(orig_access)
3106 )
3107+
3108 series_name = get_platform_info()["series"] if series is None else series
3109+ cloud_type, _ = get_cloud_type()
3110 orig_entitlement = orig_access.get("entitlement", {})
3111- overrides = orig_entitlement.pop("series", {}).pop(series_name, {})
3112- for key, value in overrides.items():
3113- current = orig_access["entitlement"].get(key)
3114- if isinstance(current, dict):
3115- # If the key already exists and is a dict, update that dict using
3116- # the override
3117- current.update(value)
3118- else:
3119- # Otherwise, replace it wholesale
3120- orig_access["entitlement"][key] = value
3121+
3122+ overrides = _select_overrides(orig_entitlement, series_name, cloud_type)
3123+
3124+ for _weight, overrides_to_apply in sorted(overrides.items()):
3125+ for key, value in overrides_to_apply.items():
3126+ current = orig_access["entitlement"].get(key)
3127+ if isinstance(current, dict):
3128+ # If the key already exists and is a dict,
3129+ # update that dict using the override
3130+ current.update(value)
3131+ else:
3132+ # Otherwise, replace it wholesale
3133+ orig_access["entitlement"][key] = value
3134
3135
3136 def del_file(path: str) -> None:
3137@@ -431,13 +476,16 @@ def prompt_choices(msg: str = "", valid_choices: List[str] = []) -> str:
3138 return value
3139
3140
3141-def prompt_for_confirmation(msg: str = "", assume_yes: bool = False) -> bool:
3142+def prompt_for_confirmation(
3143+ msg: str = "", assume_yes: bool = False, default: bool = False
3144+) -> bool:
3145 """
3146 Display a confirmation prompt, returning a bool indicating the response
3147
3148 :param msg: String custom prompt text to emit from input call.
3149 :param assume_yes: Boolean set True to skip confirmation input and return
3150 True.
3151+ :param default: Boolean to return when user doesn't enter any text
3152
3153 This function will only prompt a single time, and defaults to "no" (i.e. it
3154 returns False).
3155@@ -446,8 +494,10 @@ def prompt_for_confirmation(msg: str = "", assume_yes: bool = False) -> bool:
3156 return True
3157 if not msg:
3158 msg = status.PROMPT_YES_NO
3159- value = input(msg)
3160- if value.lower().strip() in ["y", "yes"]:
3161+ value = input(msg).lower().strip()
3162+ if value == "":
3163+ return default
3164+ if value in ["y", "yes"]:
3165 return True
3166 return False
3167
3168@@ -756,14 +806,26 @@ def redact_sensitive_logs(
3169 return redacted_log
3170
3171
3172-def should_reboot(installed_pkgs: Optional[Set[str]] = None) -> bool:
3173+def should_reboot(
3174+ installed_pkgs: Optional[Set[str]] = None,
3175+ installed_pkgs_regex: Optional[Set[str]] = None,
3176+) -> bool:
3177 """Check if the system needs to be rebooted.
3178
3179- :param installed_pkgs: If provided, verify if the any packages in
3180- the list are present on /var/run/reboot-required.pkgs. If that
3181- param is provided, we will only return true if we have the
3182- reboot-required marker file and any package in reboot-required.pkgs
3183- file.
3184+ :param installed_pkgs: If provided, verify if the any packages in
3185+ the list are present on /var/run/reboot-required.pkgs. If that
3186+ param is provided, we will only return true if we have the
3187+ reboot-required marker file and any package in reboot-required.pkgs
3188+ file. When both installed_pkgs and installed_pkgs_regex are
3189+ provided, they act as an OR, so only one of the two lists must have
3190+ a match to return True.
3191+ :param installed_pkgs_regex: If provided, verify if the any regex in
3192+ the list matches any line in /var/run/reboot-required.pkgs. If that
3193+ param is provided, we will only return true if we have the
3194+ reboot-required marker file and any match in reboot-required.pkgs
3195+ file. When both installed_pkgs and installed_pkgs_regex are
3196+ provided, they act as an OR, so only one of the two lists must have
3197+ a match to return True.
3198 """
3199
3200 # If the reboot marker file doesn't exist, we don't even
3201@@ -773,7 +835,7 @@ def should_reboot(installed_pkgs: Optional[Set[str]] = None) -> bool:
3202
3203 # If there is no installed_pkgs to check, we will rely only
3204 # on the existence of the reboot marker file
3205- if installed_pkgs is None:
3206+ if installed_pkgs is None and installed_pkgs_regex is None:
3207 return True
3208
3209 try:
3210@@ -784,8 +846,18 @@ def should_reboot(installed_pkgs: Optional[Set[str]] = None) -> bool:
3211 # If the file doesn't exist, we will default to the
3212 # reboot marker file
3213 return True
3214- else:
3215- return len(installed_pkgs.intersection(reboot_required_pkgs)) != 0
3216+
3217+ if installed_pkgs is not None:
3218+ if len(installed_pkgs.intersection(reboot_required_pkgs)) != 0:
3219+ return True
3220+
3221+ if installed_pkgs_regex is not None:
3222+ for pkg_name in reboot_required_pkgs:
3223+ for pkg_regex in installed_pkgs_regex:
3224+ if re.search(pkg_regex, pkg_name):
3225+ return True
3226+
3227+ return False
3228
3229
3230 def is_installed(package_name: str) -> bool:
3231diff --git a/uaclient/version.py b/uaclient/version.py
3232index f65a60c..b99afa0 100644
3233--- a/uaclient/version.py
3234+++ b/uaclient/version.py
3235@@ -8,7 +8,7 @@ import os.path
3236
3237 from uaclient import exceptions, util
3238
3239-__VERSION__ = "27.7"
3240+__VERSION__ = "27.8"
3241 PACKAGED_VERSION = "@@PACKAGED_VERSION@@"
3242 VERSION_TMPL = "{version}{feature_suffix}"
3243

Subscribers

People subscribed via source and target branches

to status/vote changes: