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

Proposed by Lucas Albuquerque Medeiros de Moura
Status: Needs review
Proposed branch: ~lamoura/ubuntu/+source/ubuntu-advantage-tools:upload-27.7-jammy
Merge into: ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel
Diff against target: 17950 lines (+8046/-2577)
109 files modified
Jenkinsfile (+36/-0)
Makefile (+1/-0)
README.md (+27/-6)
RELEASES.md (+14/-22)
apt-hook/json-hook-src/json-hook_test.go (+40/-40)
debian/changelog (+38/-0)
debian/ubuntu-advantage-tools.logrotate (+1/-0)
debian/ubuntu-advantage-tools.postinst (+15/-15)
features/_version.feature (+2/-2)
features/attach_invalidtoken.feature (+6/-2)
features/attach_validtoken.feature (+150/-8)
features/attached_commands.feature (+97/-73)
features/attached_enable.feature (+85/-22)
features/attached_status.feature (+2/-13)
features/aws-ids.yaml (+5/-3)
features/azure-ids.yaml (+2/-0)
features/cloud.py (+6/-3)
features/detached_auto_attach.feature (+32/-0)
features/enable_fips_container.feature (+144/-0)
features/enable_fips_vm.feature (+60/-1)
features/install_uninstall.feature (+0/-1)
features/license_check.feature (+3/-3)
features/proxy_config.feature (+24/-5)
features/schemas/ua_operation.json (+76/-0)
features/schemas/ua_security_status.json (+81/-0)
features/schemas/ua_status.json (+247/-0)
features/steps/steps.py (+138/-35)
features/ubuntu_pro.feature (+104/-9)
features/ubuntu_pro_fips.feature (+210/-0)
features/ubuntu_upgrade.feature (+3/-3)
features/ubuntu_upgrade_unattached.feature (+3/-3)
features/unattached_commands.feature (+75/-23)
features/unattached_status.feature (+100/-48)
features/util.py (+7/-0)
integration-requirements.txt (+1/-0)
lib/reboot_cmds.py (+11/-12)
setup.py (+3/-11)
sru/release-27.7/test_world_readable_logs.sh (+66/-0)
tools/create-lp-release-branches.sh (+1/-2)
tools/refresh-aws-pro-ids (+23/-12)
tools/run-integration-tests.py (+3/-4)
tox.ini (+14/-8)
uaclient/actions.py (+60/-10)
uaclient/apt.py (+70/-19)
uaclient/cli.py (+345/-162)
uaclient/clouds/gcp.py (+1/-0)
uaclient/clouds/identity.py (+2/-2)
uaclient/clouds/tests/test_aws.py (+4/-2)
uaclient/clouds/tests/test_identity.py (+9/-11)
uaclient/config.py (+119/-58)
uaclient/conftest.py (+25/-0)
uaclient/contract.py (+97/-109)
uaclient/data_types.py (+196/-0)
uaclient/defaults.py (+1/-0)
uaclient/entitlements/__init__.py (+7/-1)
uaclient/entitlements/base.py (+246/-132)
uaclient/entitlements/cc.py (+2/-3)
uaclient/entitlements/cis.py (+4/-3)
uaclient/entitlements/esm.py (+7/-4)
uaclient/entitlements/fips.py (+148/-62)
uaclient/entitlements/livepatch.py (+54/-36)
uaclient/entitlements/repo.py (+53/-38)
uaclient/entitlements/tests/test_base.py (+247/-39)
uaclient/entitlements/tests/test_cc.py (+2/-2)
uaclient/entitlements/tests/test_entitlements.py (+3/-2)
uaclient/entitlements/tests/test_esm.py (+10/-8)
uaclient/entitlements/tests/test_fips.py (+163/-57)
uaclient/entitlements/tests/test_livepatch.py (+43/-36)
uaclient/entitlements/tests/test_repo.py (+26/-32)
uaclient/event_logger.py (+221/-0)
uaclient/exceptions.py (+251/-26)
uaclient/jobs/metering.py (+2/-9)
uaclient/jobs/update_messaging.py (+21/-22)
uaclient/messages.py (+675/-0)
uaclient/security.py (+123/-86)
uaclient/security_status.py (+55/-34)
uaclient/serviceclient.py (+4/-4)
uaclient/snap.py (+5/-3)
uaclient/status.py (+38/-395)
uaclient/testing/fakes.py (+17/-0)
uaclient/tests/test_actions.py (+15/-15)
uaclient/tests/test_apt.py (+11/-14)
uaclient/tests/test_cli.py (+11/-11)
uaclient/tests/test_cli_attach.py (+413/-27)
uaclient/tests/test_cli_auto_attach.py (+7/-7)
uaclient/tests/test_cli_detach.py (+170/-24)
uaclient/tests/test_cli_disable.py (+310/-47)
uaclient/tests/test_cli_enable.py (+406/-83)
uaclient/tests/test_cli_refresh.py (+10/-10)
uaclient/tests/test_cli_security_status.py (+2/-27)
uaclient/tests/test_cli_status.py (+167/-13)
uaclient/tests/test_config.py (+79/-15)
uaclient/tests/test_contract.py (+93/-93)
uaclient/tests/test_data_types.py (+386/-0)
uaclient/tests/test_event_logger.py (+141/-0)
uaclient/tests/test_gpg.py (+9/-6)
uaclient/tests/test_lock.py (+3/-3)
uaclient/tests/test_reboot_cmds.py (+3/-3)
uaclient/tests/test_security.py (+303/-215)
uaclient/tests/test_security_status.py (+79/-19)
uaclient/tests/test_serviceclient.py (+4/-4)
uaclient/tests/test_snap.py (+7/-4)
uaclient/tests/test_update_messaging.py (+22/-36)
uaclient/tests/test_util.py (+37/-30)
uaclient/tests/test_version.py (+2/-2)
uaclient/types.py (+7/-2)
uaclient/util.py (+23/-67)
uaclient/version.py (+3/-3)
ubuntu-advantage.1 (+11/-1)
Reviewer Review Type Date Requested Status
Paride Legovini (community) Approve
Canonical Server Core Reviewers Pending
Review via email: mp+417348@code.launchpad.net

Description of the change

This MR represents release 27.7 of ubuntu-advantage-tools. We are creating this new MR because we have identified an issue in the release that needed a fix.

Originally, the MR was made through this branch:
https://code.launchpad.net/~orndorffgrant/ubuntu/+source/ubuntu-advantage-tools/+git/ubuntu-advantage-tools/+merge/416678

We have a FFE ready for this release on Jammy:
https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bug/1964134

And the SRU bug here: https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bug/1964028

To post a comment you must log in.
Revision history for this message
Paride Legovini (paride) wrote :

The bugfix commit (afc280d) LGTM. I verified the correspondence between the message strings names as called by postinst and the actual definitions.

I checked the commit hashes to make sure that the new commits are applied cleanly on the previously-reviewed branch, without rebasing. As this is the case, the new change can be considered alone, and given that it's a bugfix-only change I believe this doesn't require another FFe approval (virtually we could have uploaded the FFe approved version, followed by a bugfix-only upload, not requiring approval).

My review comment to https://code.launchpad.net/~orndorffgrant/ubuntu/+source/ubuntu-advantage-tools/+git/ubuntu-advantage-tools/+merge/416678 still applies, I'm reporting it here.

---

Looking at this as packaging for a normal new upstream release: this is fine. Packaging changes are small, the only delicate bit is the handling of the log file permissions, which is now partially handled by logrotate. If logrotate isn't installed the logs will stay non-user-readable, which is a good compromise. Lintian complains hard about E: missing-build-dependency-for-dh-addon, but the dh-systemd B-D is present as an "OR" dependency, which lintian doesn't resolve, so this is a false positive. We may want to override this, but it's not a blocker for uploading. The other Lintian warning are due to deliberate choices.

Looking at this upload from the FFe/SRU perspective: this also looks good to me. More specifically the changes are

 - Bug fixes;
 - New functionality, extending existing functionality
   in a non-breaking way;
 - Documentation changes.

A binary debdiff shows that ubuntu-advantage-tools_27.7~22.04.1_amd64.deb ships the following new files wrt 27.6~22.04.1:

Files in second .deb but not in first
-------------------------------------
-rw-r--r-- root/root /usr/lib/python3/dist-packages/uaclient/data_types.py
-rw-r--r-- root/root /usr/lib/python3/dist-packages/uaclient/event_logger.py
-rw-r--r-- root/root /usr/lib/python3/dist-packages/uaclient/messages.py

These are expected and part of the aforementioned new functionality.

I think this is ready for the "second pass" review from a SRU team member.

review: Approve
Revision history for this message
Chris Halse Rogers (raof) wrote :

SRU pass: there are a lot of changes here, but the changes to the packaging are OK and the others look like they're sensible. SRU pre-ack.

Revision history for this message
Chris Halse Rogers (raof) :

Unmerged commits

05f6b3a... by Lucas Albuquerque Medeiros de Moura

changelog: add line for postinst messages change

afc280d... by Lucas Albuquerque Medeiros de Moura

postinst: Adapt script for new messages module

bc721e4... by Renan Rodrigo

gcp: add pro license for Jammy

Signed-off-by: Renan Rodrigo <email address hidden>

9433db4... by Grant Orndorff

changelog: fix trailer line for 27.4.1

cd08064... by Lucas Albuquerque Medeiros de Moura

fix ubuntu upgrade tests for jammy

5b135d4... by Lucas Albuquerque Medeiros de Moura

fix integration test failures

feff165... by Grant Orndorff

release 27.7 changelog entry

68ca8e6... by Grant Orndorff

chore: bump version to 27.7

cf59d1d... by Renan Rodrigo

chore: bump security-status schema version to 0.1

Signed-off-by: Renan Rodrigo <email address hidden>

7db6f2d... by Renan Rodrigo

cli: include the origin site for security-status updates

Signed-off-by: Renan Rodrigo <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/Jenkinsfile b/Jenkinsfile
index f9ca868..9c71624 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -141,6 +141,27 @@ pipeline {
141 '''141 '''
142 }142 }
143 }143 }
144 stage ('Package build: 22.04') {
145 environment {
146 BUILD_SERIES = "jammy"
147 SERIES_VERSION = "22.04"
148 PKG_VERSION = sh(returnStdout: true, script: "dpkg-parsechangelog --show-field Version").trim()
149 NEW_PKG_VERSION = "${PKG_VERSION}~${SERIES_VERSION}~${JOB_SUFFIX}"
150 ARTIFACT_DIR = "${TMPDIR}${BUILD_SERIES}"
151 }
152 steps {
153 sh '''
154 set -x
155 mkdir ${ARTIFACT_DIR}
156 cp debian/changelog ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
157 sed -i "s/${PKG_VERSION}/${NEW_PKG_VERSION}/" ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
158 dpkg-source -l${WORKSPACE}/debian/changelog-${SERIES_VERSION} -b .
159 sbuild --resolve-alternatives --nolog --verbose --dist=${BUILD_SERIES} --no-run-lintian --append-to-version=~${SERIES_VERSION} ../ubuntu-advantage-tools*${NEW_PKG_VERSION}*dsc
160 cp ./ubuntu-advantage-tools*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-tools-${BUILD_SERIES}.deb
161 cp ./ubuntu-advantage-pro*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-pro-${BUILD_SERIES}.deb
162 '''
163 }
164 }
144 }165 }
145 }166 }
146 stage ('Integration Tests') {167 stage ('Integration Tests') {
@@ -190,6 +211,21 @@ pipeline {
190 '''211 '''
191 }212 }
192 }213 }
214 stage("lxc 22.04") {
215 environment {
216 UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}jammy/"
217 UACLIENT_BEHAVE_ARTIFACT_DIR = "artifacts/behave-lxd-22.04"
218 UACLIENT_BEHAVE_EPHEMERAL_INSTANCE = 1
219 UACLIENT_BEHAVE_SNAPSHOT_STRATEGY = 1
220 }
221 steps {
222 sh '''
223 set +x
224 . $TMPDIR/bin/activate
225 tox --parallel--safe-build -e behave-lxd-22.04 -- --tags="~slow"
226 '''
227 }
228 }
193 stage("lxc vm 20.04") {229 stage("lxc vm 20.04") {
194 environment {230 environment {
195 UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}focal/"231 UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}focal/"
diff --git a/Makefile b/Makefile
index e8ce957..87932a8 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,7 @@ ifneq (,$(findstring trusty,$(TOXENV)))
33endif33endif
34 pip install tox34 pip install tox
35 pip install tox-pip-version35 pip install tox-pip-version
36 pip install tox-setuptools-version
3637
37travis-deb-install:38travis-deb-install:
38 git fetch --unshallow39 git fetch --unshallow
diff --git a/README.md b/README.md
index a9040e2..8d413c5 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ Below is a list of platforms and releases ubuntu-advantage-tools supports
33| Bionic | amd64, arm64, armhf, i386, ppc64el, s390x | Active SRU of all features |33| Bionic | amd64, arm64, armhf, i386, ppc64el, s390x | Active SRU of all features |
34| Focal | amd64, arm64, armhf, ppc64el, riscv64, s390x | Active SRU of all features |34| Focal | amd64, arm64, armhf, ppc64el, riscv64, s390x | Active SRU of all features |
35| Groovy | amd64, arm64, armhf, ppc64el, riscv64, s390x | Last release 27.1 |35| Groovy | amd64, arm64, armhf, ppc64el, riscv64, s390x | Last release 27.1 |
36| Hirsute | amd64, arm64, armhf, ppc64el, riscv64, s390x | Active SRU of all features |36| Hirsute | amd64, arm64, armhf, ppc64el, riscv64, s390x | Last release 27.5 |
37| Impish | amd64, arm64, armhf, ppc64el, riscv64, s390x | Active SRU of all features |37| Impish | amd64, arm64, armhf, ppc64el, riscv64, s390x | Active SRU of all features |
3838
39Note: ppc64el will not have support for APT JSON hook messaging due to insufficient golang packages39Note: ppc64el will not have support for APT JSON hook messaging due to insufficient golang packages
@@ -107,6 +107,27 @@ https://ubuntu.com/advantage.
107* UA client auto-enables any services defined with107* UA client auto-enables any services defined with
108 `obligations:{enableByDefault: true}`108 `obligations:{enableByDefault: true}`
109109
110#### Attaching with --attach-config
111Running `ua attach` with the `--attach-config` may be better suited to certain scenarios.
112
113When using `--attach-config` the token must be passed in the file rather than on the command line. This is useful in situations where it is preffered to keep the secret token in a file.
114
115Optionally, the attach config file can be used to override the services that are automatically enabled as a part of the attach process.
116
117An attach config file looks like this:
118```yaml
119token: YOUR_TOKEN_HERE # required
120enable_services: # optional list of service names to auto-enable
121 - esm-infra
122 - esm-apps
123 - cis
124```
125
126And can be passed on the cli like this:
127```shell
128sudo ua attach --attach-config /path/to/file.yaml
129```
130
110### Enabling a service131### Enabling a service
111Each service controlled by UA client will have a python module in132Each service controlled by UA client will have a python module in
112uaclient/entitlements/\*.py which handles setup and teardown of services when133uaclient/entitlements/\*.py which handles setup and teardown of services when
@@ -367,9 +388,9 @@ when you should set UACLIENT_BEHAVE_SNAPSHOT_STRATEGY=1
367The following tox environments allow for testing focal on EC2:388The following tox environments allow for testing focal on EC2:
368389
369```390```
370 # To test ubuntu-pro-images on EC2391 # To test ubuntu-pro-images
371 tox -e behave-awspro-20.04392 tox -e behave-awspro-20.04
372 # To test Canonical cloud images (non-ubuntu-pro) on EC2393 # To test Canonical cloud images (non-ubuntu-pro)
373 tox -e behave-awsgeneric-20.04394 tox -e behave-awsgeneric-20.04
374```395```
375396
@@ -417,9 +438,9 @@ UACLIENT_BEHAVE_REUSE_IMAGE=your-custom-ami tox -e behave-awspro-20.04
417The following tox environments allow for testing focal on Azure:438The following tox environments allow for testing focal on Azure:
418439
419```440```
420 # To test ubuntu-pro-images on EC2441 # To test ubuntu-pro-images
421 tox -e behave-azurepro-20.04442 tox -e behave-azurepro-20.04
422 # To test Canonical cloud images (non-ubuntu-pro) on EC2443 # To test Canonical cloud images (non-ubuntu-pro)
423 tox -e behave-azuregeneric-20.04444 tox -e behave-azuregeneric-20.04
424```445```
425446
@@ -563,7 +584,7 @@ the project, you should install them via `dev-requirements.txt`.)
563## Daily Builds584## Daily Builds
564585
565On Launchpad, there is a [daily build recipe](https://code.launchpad.net/~canonical-server/+recipe/ua-client-daily),586On Launchpad, there is a [daily build recipe](https://code.launchpad.net/~canonical-server/+recipe/ua-client-daily),
566which will build the client and place it in the [ua-client-daily PPA](https://code.launchpad.net/~canonical-server/+archive/ubuntu/ua-client-daily).587which will build the client and place it in the [ua-client-daily PPA](https://code.launchpad.net/~ua-client/+archive/ubuntu/daily).
567588
568## Remastering custom golden images based on Ubuntu PRO589## Remastering custom golden images based on Ubuntu PRO
569590
diff --git a/RELEASES.md b/RELEASES.md
index 20096a2..3ea848a 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -226,9 +226,11 @@ If this is your first time releasing ubuntu-advantage-tools, you'll need to do t
226 lxc exec dev-i -- bash /setup_proposed.sh226 lxc exec dev-i -- bash /setup_proposed.sh
227 ```227 ```
228228
229 e. Once [ubuntu-advantage-tools shows up in the pending_sru page](https://people.canonical.com/~ubuntu-archive/pending-sru.html), perform the [Ubuntu-advantage-client SRU verification steps](https://wiki.ubuntu.com/UbuntuAdvantageToolsUpdates). This typically involves running all behave targets with `UACLIENT_BEHAVE_ENABLE_PROPOSED=1 UACLIENT_BEHAVE_CHECK_VERSION=<this-version>` and saving the output.229 e. With the package in proposed, perform the steps from `I.3` above but use a `~stableppaX` suffix instead of `~rcX` in the version name, and upload to `ppa:ua-client/stable` instead of staging.
230230
231 f. After all tests have passed, tarball all of the output files and upload them to the SRU bug with a message that looks like this:231 f. Once [ubuntu-advantage-tools shows up in the pending_sru page](https://people.canonical.com/~ubuntu-archive/pending-sru.html), perform the [Ubuntu-advantage-client SRU verification steps](https://wiki.ubuntu.com/UbuntuAdvantageToolsUpdates). This typically involves running all behave targets with `UACLIENT_BEHAVE_ENABLE_PROPOSED=1 UACLIENT_BEHAVE_CHECK_VERSION=<this-version>` and saving the output.
232
233 g. After all tests have passed, tarball all of the output files and upload them to the SRU bug with a message that looks like this:
232 ```234 ```
233 We have run the full ubuntu-advantage-tools integration test suite against the version in -proposed. The results are attached. All tests passed (or call out specific explained failures).235 We have run the full ubuntu-advantage-tools integration test suite against the version in -proposed. The results are attached. All tests passed (or call out specific explained failures).
234 236
@@ -238,33 +240,23 @@ If this is your first time releasing ubuntu-advantage-tools, you'll need to do t
238 ```240 ```
239 Change the tags on the bug from `verification-needed` to `verification-done` (including the verification tags for each release).241 Change the tags on the bug from `verification-needed` to `verification-done` (including the verification tags for each release).
240242
241 g. For any other related Launchpad bugs that are fixed in this release. Perform the verification steps necessary for those bugs and mark them `verification-done` as needed. This will likely involve following the test steps, but instead of adding the staging PPA, enabling -proposed.243 h. For any other related Launchpad bugs that are fixed in this release. Perform the verification steps necessary for those bugs and mark them `verification-done` as needed. This will likely involve following the test steps, but instead of adding the staging PPA, enabling -proposed.
242244
243 h. Once all SRU bugs are tagged as `verification*-done`, all SRU-bugs should be listed as green in [the pending_sru page](https://people.canonical.com/~ubuntu-archive/pending-sru.html).245 i. Once all SRU bugs are tagged as `verification*-done`, all SRU-bugs should be listed as green in [the pending_sru page](https://people.canonical.com/~ubuntu-archive/pending-sru.html).
244 246
245 i. After the pending sru page says that ubuntu-advantage-tools has been in proposed for 7 days, it is now time to ping the [current SRU vanguard](https://wiki.ubuntu.com/StableReleaseUpdates#Publishing) for acceptance of ubuntu-advantage-tools into -updates.247 j. After the pending sru page says that ubuntu-advantage-tools has been in proposed for 7 days, it is now time to ping the [current SRU vanguard](https://wiki.ubuntu.com/StableReleaseUpdates#Publishing) for acceptance of ubuntu-advantage-tools into -updates.
246248
247 j. Ping the Ubuntu Server team member who approved the version in step `II.4` to now upload to the devel release.249 k. Ping the Ubuntu Server team member who approved the version in step `II.4` to now upload to the devel release.
248250
249 k. Check `rmadison ubuntu-advantage-tools` for updated version in devel release251 l. Check `rmadison ubuntu-advantage-tools` for updated version in devel release
250252
251 l. Confirm availability in <devel-series>-updates pocket via `lxc launch ubuntu-daily:<devel-series> dev-i; lxc exec dev-i -- apt update; lxc exec dev-i -- apt-cache policy ubuntu-advantage-tools`253 m. Confirm availability in <devel-series>-updates pocket via `lxc launch ubuntu-daily:<devel-series> dev-i; lxc exec dev-i -- apt update; lxc exec dev-i -- apt-cache policy ubuntu-advantage-tools`
252254
253### III. Final release to team infrastructure255### III. Github Repository Post-release Update
254256
2551. Ensure the version tag is correct on github. The `version` git tag should point to the commit that was released as that version to ubuntu -updates. If changes were made in response to feedback during the release process, the tag may have to be moved.2571. Ensure the version tag is correct on github. The `version` git tag should point to the commit that was released as that version to ubuntu -updates. If changes were made in response to feedback during the release process, the tag may have to be moved.
2562. Perform the steps from `I.3` above but use a `~stableppaX` suffix instead of `~rcX` in the version name, and upload to `ppa:ua-client/stable` instead of staging.2582. Bring in any changes that were made to the release branch into `main` via PR (e.g. Changelog edits).
2573. Bring in any changes that were made to the release branch into `main` via PR (e.g. Changelog edits).
258
259## Ubuntu PRO Release Process
260259
261Below is the procedure used to release ubuntu-advantage-tools to Ubuntu PRO images:260## Cloud Images Update
262261
263 1. [Open Daily PPA copy-package operation](https://code.launchpad.net/~ua-client/+archive/ubuntu/daily/+copy-packages)262After the release process is finished, CPC must be informed. They will be responsible to update the cloud images using the package from the pockets it was released to (whether it is the `stable` PPA or the`-updates` pocket).
264 2. Check Xenial, Bionic, Focal, Hirsute, Impish packages
265 3. Select Destination PPA: UA Client Premium [~ua-client/ubuntu/staging]
266 4. Select Destination series: The same series
267 5. Copy options: "Copy existing binaries"
268 6. Click Copy packages
269 7. Notify Pro Image creators about expected Premium PPA version (patviafore/powersj)
270 8. Once new PRO AMIs are publicly available run `./tools/refresh-aws-pro-ids` to update AMIs we test during CI runs
diff --git a/apt-hook/json-hook-src/json-hook_test.go b/apt-hook/json-hook-src/json-hook_test.go
index 0a2a2eb..695f323 100644
--- a/apt-hook/json-hook-src/json-hook_test.go
+++ b/apt-hook/json-hook-src/json-hook_test.go
@@ -114,9 +114,9 @@ const mockJson = `
114 "pin": 500,114 "pin": 500,
115 "origins": [115 "origins": [
116 {116 {
117 "archive": "hirsute-apps-security",117 "archive": "focal-apps-security",
118 "codename": "hirsute",118 "codename": "focal",
119 "version": "21.04",119 "version": "20.04",
120 "origin": "UbuntuESMApps",120 "origin": "UbuntuESMApps",
121 "label": "Ubuntu",121 "label": "Ubuntu",
122 "site": ""122 "site": ""
@@ -130,9 +130,9 @@ const mockJson = `
130 "pin": 500,130 "pin": 500,
131 "origins": [131 "origins": [
132 {132 {
133 "archive": "hirsute-apps-security",133 "archive": "focal-apps-security",
134 "codename": "hirsute",134 "codename": "focal",
135 "version": "21.04",135 "version": "20.04",
136 "origin": "UbuntuESMApps",136 "origin": "UbuntuESMApps",
137 "label": "Ubuntu",137 "label": "Ubuntu",
138 "site": ""138 "site": ""
@@ -162,9 +162,9 @@ const mockJson = `
162 "pin": 500,162 "pin": 500,
163 "origins": [163 "origins": [
164 {164 {
165 "archive": "hirsute-apps-security",165 "archive": "focal-apps-security",
166 "codename": "hirsute",166 "codename": "focal",
167 "version": "21.04",167 "version": "20.04",
168 "origin": "UbuntuESMApps",168 "origin": "UbuntuESMApps",
169 "label": "Ubuntu",169 "label": "Ubuntu",
170 "site": ""170 "site": ""
@@ -178,9 +178,9 @@ const mockJson = `
178 "pin": 500,178 "pin": 500,
179 "origins": [179 "origins": [
180 {180 {
181 "archive": "hirsute-apps-security",181 "archive": "focal-apps-security",
182 "codename": "hirsute",182 "codename": "focal",
183 "version": "21.04",183 "version": "20.04",
184 "origin": "UbuntuESMApps",184 "origin": "UbuntuESMApps",
185 "label": "Ubuntu",185 "label": "Ubuntu",
186 "site": ""186 "site": ""
@@ -210,17 +210,17 @@ const mockJson = `
210 "pin": 500,210 "pin": 500,
211 "origins": [211 "origins": [
212 {212 {
213 "archive": "hirsute-infra-security",213 "archive": "focal-infra-security",
214 "codename": "hirsute",214 "codename": "focal",
215 "version": "21.04",215 "version": "20.04",
216 "origin": "UbuntuESM",216 "origin": "UbuntuESM",
217 "label": "Ubuntu",217 "label": "Ubuntu",
218 "site": ""218 "site": ""
219 },219 },
220 {220 {
221 "archive": "hirsute",221 "archive": "focal",
222 "codename": "hirsute",222 "codename": "focal",
223 "version": "21.04",223 "version": "20.04",
224 "origin": "Ubuntu",224 "origin": "Ubuntu",
225 "label": "Ubuntu",225 "label": "Ubuntu",
226 "site": ""226 "site": ""
@@ -234,17 +234,17 @@ const mockJson = `
234 "pin": 500,234 "pin": 500,
235 "origins": [235 "origins": [
236 {236 {
237 "archive": "hirsute-infra-security",237 "archive": "focal-infra-security",
238 "codename": "hirsute",238 "codename": "focal",
239 "version": "21.04",239 "version": "20.04",
240 "origin": "UbuntuESM",240 "origin": "UbuntuESM",
241 "label": "Ubuntu",241 "label": "Ubuntu",
242 "site": ""242 "site": ""
243 },243 },
244 {244 {
245 "archive": "hirsute",245 "archive": "focal",
246 "codename": "hirsute",246 "codename": "focal",
247 "version": "21.04",247 "version": "20.04",
248 "origin": "Ubuntu",248 "origin": "Ubuntu",
249 "label": "Ubuntu",249 "label": "Ubuntu",
250 "site": ""250 "site": ""
@@ -274,9 +274,9 @@ const mockJson = `
274 "pin": 500,274 "pin": 500,
275 "origins": [275 "origins": [
276 {276 {
277 "archive": "hirsute-infra-security",277 "archive": "focal-infra-security",
278 "codename": "hirsute",278 "codename": "focal",
279 "version": "21.04",279 "version": "20.04",
280 "origin": "UbuntuESM",280 "origin": "UbuntuESM",
281 "label": "Ubuntu",281 "label": "Ubuntu",
282 "site": ""282 "site": ""
@@ -290,9 +290,9 @@ const mockJson = `
290 "pin": 500,290 "pin": 500,
291 "origins": [291 "origins": [
292 {292 {
293 "archive": "hirsute-infra-security",293 "archive": "focal-infra-security",
294 "codename": "hirsute",294 "codename": "focal",
295 "version": "21.04",295 "version": "20.04",
296 "origin": "UbuntuESM",296 "origin": "UbuntuESM",
297 "label": "Ubuntu",297 "label": "Ubuntu",
298 "site": ""298 "site": ""
@@ -322,8 +322,8 @@ const mockJson = `
322 "pin": 500,322 "pin": 500,
323 "origins": [323 "origins": [
324 {324 {
325 "archive": "hirsute-apps-security",325 "archive": "focal-apps-security",
326 "codename": "hirsute",326 "codename": "focal",
327 "version": "1.0",327 "version": "1.0",
328 "origin": "UbuntuESMApps",328 "origin": "UbuntuESMApps",
329 "label": "Google",329 "label": "Google",
@@ -338,8 +338,8 @@ const mockJson = `
338 "pin": 500,338 "pin": 500,
339 "origins": [339 "origins": [
340 {340 {
341 "archive": "hirsute-apps-security",341 "archive": "focal-apps-security",
342 "codename": "hirsute",342 "codename": "focal",
343 "version": "1.0",343 "version": "1.0",
344 "origin": "UbuntuESMApps",344 "origin": "UbuntuESMApps",
345 "label": "Google",345 "label": "Google",
@@ -370,9 +370,9 @@ const mockJson = `
370 "pin": 500,370 "pin": 500,
371 "origins": [371 "origins": [
372 {372 {
373 "archive": "hirsute-security",373 "archive": "focal-security",
374 "codename": "hirsute",374 "codename": "focal",
375 "version": "21.04",375 "version": "20.04",
376 "origin": "Ubuntu",376 "origin": "Ubuntu",
377 "label": "Ubuntu",377 "label": "Ubuntu",
378 "site": ""378 "site": ""
@@ -386,9 +386,9 @@ const mockJson = `
386 "pin": 500,386 "pin": 500,
387 "origins": [387 "origins": [
388 {388 {
389 "archive": "hirsute-security",389 "archive": "focal-security",
390 "codename": "hirsute",390 "codename": "focal",
391 "version": "21.04",391 "version": "20.04",
392 "origin": "Ubuntu",392 "origin": "Ubuntu",
393 "label": "Ubuntu",393 "label": "Ubuntu",
394 "site": ""394 "site": ""
diff --git a/debian/changelog b/debian/changelog
index 6feeb98..b44d4d9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,41 @@
1ubuntu-advantage-tools (27.7~22.04.1) jammy; urgency=medium
2
3 * d/changelog:
4 - fix changelog trailer line for 27.4.1
5 * d/logrotate:
6 - make new logs world readable
7 * d/tools.postinst:
8 - refactor to catch exception from entitlement_factory
9 - no longer always set log file to only root readable
10 - when creating log file for the first time, make world readable
11 - adapt postinst for new messages module
12 * New upstream release 27.7 (LP: #1964028)
13 - attach: --attach-config option for customizing auto-enabled services
14 and supplying token via a file
15 - auto-attach: fix bug where auto-attach caused a manually attached
16 machine to detach
17 - cli:
18 + support --format=json for attach
19 + support --format=json for detach
20 + support --format=json for enable
21 + support --format=json for disable
22 - contract: include activity info when updating contract
23 - detach: no longer contacts contract server on detach
24 - fips: allow fips on containers
25 - fix: support USNs that don't have related CVEs
26 - logs: make all newly created logs world-readable
27 - security-status:
28 + show already installed esm package counts
29 + include APT origin for each potential update
30 + bump schema version to "0.1"
31 + remove previously required --beta flag
32 - status:
33 + include blocked_by information in service status when format=json
34 + --simulate-with-token now reports expired tokens as errors
35 + --simulate-with-token now returns errors in the specified format
36
37 -- Grant Orndorff <grant.orndorff@canonical.com> Mon, 07 Mar 2022 13:14:57 -0500
38
1ubuntu-advantage-tools (27.6~22.04.1) jammy; urgency=medium39ubuntu-advantage-tools (27.6~22.04.1) jammy; urgency=medium
240
3 * New upstream release 27.6 (LP: #1958556)41 * New upstream release 27.6 (LP: #1958556)
diff --git a/debian/ubuntu-advantage-tools.logrotate b/debian/ubuntu-advantage-tools.logrotate
index 76e6b47..7c64857 100644
--- a/debian/ubuntu-advantage-tools.logrotate
+++ b/debian/ubuntu-advantage-tools.logrotate
@@ -2,6 +2,7 @@
2# of /var/log/ubuntu-advantage*.log files.2# of /var/log/ubuntu-advantage*.log files.
3/var/log/ubuntu-advantage*.log {3/var/log/ubuntu-advantage*.log {
4 su root root4 su root root
5 create 0644 root root
5 rotate 66 rotate 6
6 monthly7 monthly
7 compress8 compress
diff --git a/debian/ubuntu-advantage-tools.postinst b/debian/ubuntu-advantage-tools.postinst
index 191f0db..c7e1d02 100644
--- a/debian/ubuntu-advantage-tools.postinst
+++ b/debian/ubuntu-advantage-tools.postinst
@@ -119,12 +119,12 @@ check_service_is_beta() {
119 _IS_BETA_SVC=$(/usr/bin/python3 -c "119 _IS_BETA_SVC=$(/usr/bin/python3 -c "
120from uaclient.config import UAConfig120from uaclient.config import UAConfig
121from uaclient.entitlements import entitlement_factory121from uaclient.entitlements import entitlement_factory
122ent_cls = entitlement_factory('${service_name}')122try:
123if ent_cls:123 ent_cls = entitlement_factory('${service_name}')
124 cfg = UAConfig()124 cfg = UAConfig()
125 allow_beta = cfg.features.get('allow_beta', False)125 allow_beta = cfg.features.get('allow_beta', False)
126 print(all([ent_cls.is_beta, not allow_beta]))126 print(all([ent_cls.is_beta, not allow_beta]))
127else:127except Exception:
128 print(True)128 print(True)
129")129")
130if [ "${_IS_BETA_SVC}" = "True" ]; then130if [ "${_IS_BETA_SVC}" = "True" ]; then
@@ -248,16 +248,17 @@ configure_esm() {
248mark_reboot_for_fips_pro() {248mark_reboot_for_fips_pro() {
249 FIPS_HOLDS=$(apt-mark showholds | grep -E 'fips|libssl1|openssh-client|openssh-server|linux-fips|openssl|strongswan' || exit 0)249 FIPS_HOLDS=$(apt-mark showholds | grep -E 'fips|libssl1|openssh-client|openssh-server|linux-fips|openssl|strongswan' || exit 0)
250 if [ "$FIPS_HOLDS" ]; then250 if [ "$FIPS_HOLDS" ]; then
251 mark_reboot_cmds_as_needed MESSAGE_FIPS_REBOOT_REQUIRED251 mark_reboot_cmds_as_needed FIPS_REBOOT_REQUIRED_MSG
252 fi252 fi
253}253}
254254
255255
256add_notice() {256add_notice() {
257 msg_name=$1257 module=$1
258 msg_name=$2
258 /usr/bin/python3 -c "259 /usr/bin/python3 -c "
259from uaclient.config import UAConfig260from uaclient.config import UAConfig
260from uaclient.status import ${msg_name}261from uaclient.${module} import ${msg_name}
261cfg = UAConfig()262cfg = UAConfig()
262cfg.add_notice(label='', description=${msg_name})263cfg.add_notice(label='', description=${msg_name})
263"264"
@@ -268,7 +269,7 @@ mark_reboot_cmds_as_needed() {
268 if [ ! -f "$REBOOT_CMD_MARKER_FILE" ]; then269 if [ ! -f "$REBOOT_CMD_MARKER_FILE" ]; then
269 touch $REBOOT_CMD_MARKER_FILE270 touch $REBOOT_CMD_MARKER_FILE
270 fi271 fi
271 add_notice "$msg_name"272 add_notice messages "$msg_name"
272}273}
273274
274patch_status_json_0_1_for_non_root() {275patch_status_json_0_1_for_non_root() {
@@ -303,7 +304,7 @@ notify_wrong_fips_metapackage_on_cloud() {
303304
304 if echo "$cloud_id" | grep -E -q "^(azure|aws)"; then305 if echo "$cloud_id" | grep -E -q "^(azure|aws)"; then
305 if echo "$fips_installed" | grep -E -q "installed"; then306 if echo "$fips_installed" | grep -E -q "installed"; then
306 add_notice NOTICE_WRONG_FIPS_METAPACKAGE_ON_CLOUD307 add_notice status NOTICE_WRONG_FIPS_METAPACKAGE_ON_CLOUD
307 fi308 fi
308 fi309 fi
309}310}
@@ -399,7 +400,7 @@ case "$1" in
399 # Repo for FIPS packages changed from old client400 # Repo for FIPS packages changed from old client
400 if [ -f $FIPS_APT_SOURCE_FILE ]; then401 if [ -f $FIPS_APT_SOURCE_FILE ]; then
401 if grep -q $OLD_CLIENT_FIPS_PPA $FIPS_APT_SOURCE_FILE; then402 if grep -q $OLD_CLIENT_FIPS_PPA $FIPS_APT_SOURCE_FILE; then
402 add_notice MESSAGE_FIPS_INSTALL_OUT_OF_DATE403 add_notice messages FIPS_INSTALL_OUT_OF_DATE
403 fi404 fi
404 fi405 fi
405406
@@ -419,15 +420,14 @@ case "$1" in
419 fi420 fi
420 fi421 fi
421422
422 # We modify permissions for ubuntu-advantage.log because423 # log files need to be world-readable
423 # in a past version of UA, this log file was world readable.
424 # This isn't necessary for the timer/license-check log files,
425 # because they have always been only root-readable.
426 if [ ! -f /var/log/ubuntu-advantage.log ]; then424 if [ ! -f /var/log/ubuntu-advantage.log ]; then
427 touch /var/log/ubuntu-advantage.log425 touch /var/log/ubuntu-advantage.log
426 # We are only making new log files world readable
427 chmod 0644 /var/log/ubuntu-advantage.log
428 fi428 fi
429 chmod 0600 /var/log/ubuntu-advantage.log
430 chown root:root /var/log/ubuntu-advantage.log429 chown root:root /var/log/ubuntu-advantage.log
430
431 private_dir="/var/lib/ubuntu-advantage/private"431 private_dir="/var/lib/ubuntu-advantage/private"
432 if [ -d "$private_dir" ]; then432 if [ -d "$private_dir" ]; then
433 chmod 0700 "$private_dir"433 chmod 0700 "$private_dir"
@@ -435,7 +435,7 @@ case "$1" in
435435
436 if [ "$VERSION_ID" = "16.04" ]; then436 if [ "$VERSION_ID" = "16.04" ]; then
437 if echo "$PREVIOUS_PKG_VER" | grep -q "14.04"; then437 if echo "$PREVIOUS_PKG_VER" | grep -q "14.04"; then
438 mark_reboot_cmds_as_needed MESSAGE_LIVEPATCH_LTS_REBOOT_REQUIRED438 mark_reboot_cmds_as_needed LIVEPATCH_LTS_REBOOT_REQUIRED
439 fi439 fi
440 fi440 fi
441 mark_reboot_for_fips_pro441 mark_reboot_for_fips_pro
diff --git a/features/_version.feature b/features/_version.feature
index e265769..b972500 100644
--- a/features/_version.feature
+++ b/features/_version.feature
@@ -6,8 +6,10 @@ Feature: UA is expected version
6 @uses.config.machine_type.lxd.vm6 @uses.config.machine_type.lxd.vm
7 @uses.config.machine_type.aws.generic7 @uses.config.machine_type.aws.generic
8 @uses.config.machine_type.aws.pro8 @uses.config.machine_type.aws.pro
9 @uses.config.machine_type.aws.pro.fips
9 @uses.config.machine_type.azure.generic10 @uses.config.machine_type.azure.generic
10 @uses.config.machine_type.azure.pro11 @uses.config.machine_type.azure.pro
12 @uses.config.machine_type.azure.pro.fips
11 @uses.config.machine_type.gcp.generic13 @uses.config.machine_type.gcp.generic
12 @uses.config.machine_type.gcp.pro14 @uses.config.machine_type.gcp.pro
13 Scenario Outline: Check ua version15 Scenario Outline: Check ua version
@@ -30,7 +32,6 @@ Feature: UA is expected version
30 | xenial |32 | xenial |
31 | bionic |33 | bionic |
32 | focal |34 | focal |
33 | hirsute |
34 | impish |35 | impish |
35 | jammy |36 | jammy |
3637
@@ -58,5 +59,4 @@ Feature: UA is expected version
58 | xenial |59 | xenial |
59 | bionic |60 | bionic |
60 | focal |61 | focal |
61 | hirsute |
62 | impish |62 | impish |
diff --git a/features/attach_invalidtoken.feature b/features/attach_invalidtoken.feature
index 6ad2d93..59435c2 100644
--- a/features/attach_invalidtoken.feature
+++ b/features/attach_invalidtoken.feature
@@ -15,12 +15,17 @@ Feature: Command behaviour when trying to attach a machine to an Ubuntu
15 """15 """
16 This command must be run as root (try using sudo).16 This command must be run as root (try using sudo).
17 """17 """
18 When I verify that running `ua attach invalid-token --format json` `with sudo` exits `1`
19 Then I will see the following on stdout:
20 """
21 {"_schema_version": "0.1", "errors": [{"message": "Invalid token. See https://ubuntu.com/advantage", "message_code": "attach-invalid-token", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
22 """
23
18 Examples: ubuntu release24 Examples: ubuntu release
19 | release |25 | release |
20 | xenial |26 | xenial |
21 | bionic |27 | bionic |
22 | focal |28 | focal |
23 | hirsute |
24 | impish |29 | impish |
25 | jammy |30 | jammy |
2631
@@ -41,6 +46,5 @@ Feature: Command behaviour when trying to attach a machine to an Ubuntu
41 | xenial |46 | xenial |
42 | bionic |47 | bionic |
43 | focal |48 | focal |
44 | hirsute |
45 | impish |49 | impish |
46 | jammy |50 | jammy |
diff --git a/features/attach_validtoken.feature b/features/attach_validtoken.feature
index 8045591..25f763b 100644
--- a/features/attach_validtoken.feature
+++ b/features/attach_validtoken.feature
@@ -3,7 +3,6 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
3 subscription using a valid token3 subscription using a valid token
44
5 @series.jammy5 @series.jammy
6 @series.hirsute
7 @series.impish6 @series.impish
8 @uses.config.machine_type.lxd.container7 @uses.config.machine_type.lxd.container
9 Scenario Outline: Attached command in a non-lts ubuntu machine8 Scenario Outline: Attached command in a non-lts ubuntu machine
@@ -24,7 +23,6 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
2423
25 Examples: ubuntu release24 Examples: ubuntu release
26 | release |25 | release |
27 | hirsute |
28 | impish |26 | impish |
29 | jammy |27 | jammy |
3028
@@ -67,8 +65,8 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
67 """65 """
68 esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)66 esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
69 esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)67 esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
70 fips +yes +n/a +NIST-certified core packages68 fips +yes +disabled +NIST-certified core packages
71 fips-updates +yes +n/a +NIST-certified core packages with priority security updates69 fips-updates +yes +disabled +NIST-certified core packages with priority security updates
72 livepatch +yes +n/a +Canonical Livepatch service70 livepatch +yes +n/a +Canonical Livepatch service
73 """71 """
74 And stdout matches regexp:72 And stdout matches regexp:
@@ -173,6 +171,115 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
173 | bionic | libkrad0=1.16-2build1 | disabled | cis |171 | bionic | libkrad0=1.16-2build1 | disabled | cis |
174 | focal | hello=2.10-2ubuntu2 | n/a | usg |172 | focal | hello=2.10-2ubuntu2 | n/a | usg |
175173
174 @series.lts
175 @uses.config.machine_type.lxd.container
176 Scenario Outline: Attach command with attach config
177 Given a `<release>` machine with ubuntu-advantage-tools installed
178 # simplest happy path
179 When I create the file `/tmp/attach.yaml` with the following
180 """
181 token: <contract_token>
182 """
183 When I replace `<contract_token>` in `/tmp/attach.yaml` with token `contract_token`
184 When I run `ua attach --attach-config /tmp/attach.yaml` with sudo
185 Then stdout matches regexp:
186 """
187 esm-apps +yes +enabled
188 """
189 And stdout matches regexp:
190 """
191 esm-infra +yes +enabled
192 """
193 And stdout matches regexp:
194 """
195 <cis_or_usg> +yes +disabled
196 """
197 When I run `ua detach --assume-yes` with sudo
198 # don't allow both token on cli and config
199 Then I verify that running `ua attach TOKEN --attach-config /tmp/attach.yaml` `with sudo` exits `1`
200 Then stderr matches regexp:
201 """
202 Do not pass the TOKEN arg if you are using --attach-config.
203 Include the token in the attach-config file instead.
204 """
205 # happy path with service overrides
206 When I create the file `/tmp/attach.yaml` with the following
207 """
208 token: <contract_token>
209 enable_services:
210 - esm-apps
211 - <cis_or_usg>
212 """
213 When I replace `<contract_token>` in `/tmp/attach.yaml` with token `contract_token`
214 When I run `ua attach --attach-config /tmp/attach.yaml` with sudo
215 Then stdout matches regexp:
216 """
217 esm-apps +yes +enabled
218 """
219 And stdout matches regexp:
220 """
221 esm-infra +yes +disabled
222 """
223 And stdout matches regexp:
224 """
225 <cis_or_usg> +yes +enabled
226 """
227 When I run `ua detach --assume-yes` with sudo
228 # missing token
229 When I create the file `/tmp/attach.yaml` with the following
230 """
231 enable_services:
232 - esm-apps
233 - <cis_or_usg>
234 """
235 Then I verify that running `ua attach --attach-config /tmp/attach.yaml` `with sudo` exits `1`
236 Then stderr matches regexp:
237 """
238 Error while reading /tmp/attach.yaml: Got value with incorrect type for field
239 "token": Expected value with type StringDataValue but got value: None
240 """
241 # other schema error
242 When I create the file `/tmp/attach.yaml` with the following
243 """
244 token: <contract_token>
245 enable_services: {cis: true}
246 """
247 When I replace `<contract_token>` in `/tmp/attach.yaml` with token `contract_token`
248 Then I verify that running `ua attach --attach-config /tmp/attach.yaml` `with sudo` exits `1`
249 Then stderr matches regexp:
250 """
251 Error while reading /tmp/attach.yaml: Got value with incorrect type for field
252 "enable_services": Expected value with type list but got value: {\'cis\': True}
253 """
254 # invalid service name
255 When I create the file `/tmp/attach.yaml` with the following
256 """
257 token: <contract_token>
258 enable_services:
259 - esm-apps
260 - nonexistent
261 - nonexistent2
262 """
263 When I replace `<contract_token>` in `/tmp/attach.yaml` with token `contract_token`
264 Then I verify that running `ua attach --attach-config /tmp/attach.yaml` `with sudo` exits `1`
265 Then stdout matches regexp:
266 """
267 esm-apps +yes +enabled
268 """
269 And stdout matches regexp:
270 """
271 esm-infra +yes +disabled
272 """
273 Then stderr matches regexp:
274 """
275 Cannot enable unknown service 'nonexistent, nonexistent2'.
276 """
277 Examples: ubuntu
278 | release | cis_or_usg |
279 | xenial | cis |
280 | bionic | cis |
281 | focal | usg |
282
176 @series.all283 @series.all
177 @uses.config.machine_type.aws.generic284 @uses.config.machine_type.aws.generic
178 Scenario Outline: Attach command in an generic AWS Ubuntu VM285 Scenario Outline: Attach command in an generic AWS Ubuntu VM
@@ -231,7 +338,7 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
231 | release | fips_status |lp_status | lp_desc | cc_status | cis_or_usg |338 | release | fips_status |lp_status | lp_desc | cc_status | cis_or_usg |
232 | xenial | disabled |enabled | Canonical Livepatch service | disabled | cis |339 | xenial | disabled |enabled | Canonical Livepatch service | disabled | cis |
233 | bionic | disabled |enabled | Canonical Livepatch service | disabled | cis |340 | bionic | disabled |enabled | Canonical Livepatch service | disabled | cis |
234 | focal | n/a |enabled | Canonical Livepatch service | n/a | usg |341 | focal | disabled |enabled | Canonical Livepatch service | n/a | usg |
235342
236 @series.all343 @series.all
237 @uses.config.machine_type.azure.generic344 @uses.config.machine_type.azure.generic
@@ -290,8 +397,8 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
290 Examples: ubuntu release livepatch status397 Examples: ubuntu release livepatch status
291 | release | lp_status | fips_status | cc_status | cis_or_usg |398 | release | lp_status | fips_status | cc_status | cis_or_usg |
292 | xenial | enabled | n/a | disabled | cis |399 | xenial | enabled | n/a | disabled | cis |
293 | bionic | n/a | disabled | disabled | cis |400 | bionic | enabled | disabled | disabled | cis |
294 | focal | enabled | n/a | n/a | usg |401 | focal | enabled | disabled | n/a | usg |
295402
296 @series.all403 @series.all
297 @uses.config.machine_type.gcp.generic404 @uses.config.machine_type.gcp.generic
@@ -351,4 +458,39 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
351 | release | lp_status | fips_status | cc_status | cis_or_usg |458 | release | lp_status | fips_status | cc_status | cis_or_usg |
352 | xenial | n/a | n/a | disabled | cis |459 | xenial | n/a | n/a | disabled | cis |
353 | bionic | n/a | disabled | disabled | cis |460 | bionic | n/a | disabled | disabled | cis |
354 | focal | enabled | n/a | n/a | usg |461 | focal | enabled | disabled | n/a | usg |
462
463 @series.all
464 @uses.config.machine_type.lxd.container
465 Scenario Outline: Attach command with json output
466 Given a `<release>` machine with ubuntu-advantage-tools installed
467 When I verify that running attach `as non-root` with json response exits `1`
468 Then I will see the following on stdout:
469 """
470 {"_schema_version": "0.1", "errors": [{"message": "This command must be run as root (try using sudo).", "message_code": "nonroot-user", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
471 """
472 When I verify that running attach `with sudo` with json response exits `0`
473 Then I will see the following on stdout:
474 """
475 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-apps", "esm-infra"], "result": "success", "warnings": []}
476 """
477 When I run `ua status` with sudo
478 Then stdout matches regexp:
479 """
480 SERVICE ENTITLED STATUS DESCRIPTION
481 cc-eal +yes +<cc-eal> +Common Criteria EAL2 Provisioning Packages
482 """
483 And stdout matches regexp:
484 """
485 esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
486 esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
487 fips +yes +disabled +NIST-certified core packages
488 fips-updates +yes +disabled +NIST-certified core packages with priority security updates
489 livepatch +yes +n/a +Canonical Livepatch service
490 """
491
492 Examples: ubuntu release
493 | release | cc-eal |
494 | xenial | disabled |
495 | bionic | disabled |
496 | focal | n/a |
diff --git a/features/attached_commands.feature b/features/attached_commands.feature
index ca536dd..dd6cf1a 100644
--- a/features/attached_commands.feature
+++ b/features/attached_commands.feature
@@ -38,7 +38,9 @@ Feature: Command behaviour when attached to an UA subscription
38 And I run `sh -c "ls /var/log/ubuntu-advantage* | sort -d"` as non-root38 And I run `sh -c "ls /var/log/ubuntu-advantage* | sort -d"` as non-root
39 Then stdout matches regexp:39 Then stdout matches regexp:
40 """40 """
41 /var/log/ubuntu-advantage.log
41 /var/log/ubuntu-advantage.log.142 /var/log/ubuntu-advantage.log.1
43 /var/log/ubuntu-advantage-timer.log
42 /var/log/ubuntu-advantage-timer.log.144 /var/log/ubuntu-advantage-timer.log.1
43 """45 """
4446
@@ -47,79 +49,89 @@ Feature: Command behaviour when attached to an UA subscription
47 | bionic |49 | bionic |
48 | focal |50 | focal |
49 | xenial |51 | xenial |
50 | hirsute |
51 | impish |52 | impish |
52 | jammy |53 | jammy |
5354
54 @series.all
55 @uses.config.machine_type.lxd.container
56 Scenario Outline: Attached and detach correctly reach contract endpoint
57 Given a `<release>` machine with ubuntu-advantage-tools installed
58 When I attach `contract_token` with sudo
59 And I run `ua detach --assume-yes` with sudo
60 Then I verify that running `grep "Found new machine-id. Do not call detach on contract backend" /var/log/ubuntu-advantage.log` `with sudo` exits `1`
61
62 Examples: ubuntu release
63 | release |
64 | bionic |
65 | focal |
66 | xenial |
67 | hirsute |
68 | impish |
69 | jammy |
7055
71 @series.all56 @series.all
72 @uses.config.machine_type.lxd.container57 @uses.config.machine_type.lxd.container
73 Scenario Outline: Attached and detach don't reach contract endpoint if machine-id changes58 Scenario Outline: Attached disable of an already disabled service in a ubuntu machine
74 Given a `<release>` machine with ubuntu-advantage-tools installed59 Given a `<release>` machine with ubuntu-advantage-tools installed
75 When I attach `contract_token` with sudo60 When I attach `contract_token` with sudo
76 And I update contract to use `machineId` as `new-machine-id`61 Then I verify that running `ua disable livepatch` `as non-root` exits `1`
77 And I run `ua detach --assume-yes` with sudo62 And stderr matches regexp:
78 Then stdout matches regexp:
79 """63 """
80 This machine is now detached.64 This command must be run as root \(try using sudo\).
65 """
66 And I verify that running `ua disable livepatch` `with sudo` exits `1`
67 And I will see the following on stdout:
68 """
69 Livepatch is not currently enabled
70 See: sudo ua status
81 """71 """
82 And I verify that running `grep "Found new machine-id. Do not call detach on contract backend" /var/log/ubuntu-advantage.log` `with sudo` exits `0`
83 When I run `ua status` with sudo
84 Then stdout matches regexp:
85 """
86 This machine is not attached to a UA subscription.
87 """
8872
89 Examples: ubuntu release73 Examples: ubuntu release
90 | release |74 | release |
91 | bionic |75 | bionic |
92 | focal |76 | focal |
93 | xenial |77 | xenial |
94 | hirsute |
95 | impish |78 | impish |
96 | jammy |79 | jammy |
9780
98 @series.all81 @series.lts
99 @uses.config.machine_type.lxd.container82 @uses.config.machine_type.lxd.container
100 Scenario Outline: Attached disable of an already disabled service in a ubuntu machine83 Scenario Outline: Attached disable with json format
101 Given a `<release>` machine with ubuntu-advantage-tools installed84 Given a `<release>` machine with ubuntu-advantage-tools installed
102 When I attach `contract_token` with sudo85 When I attach `contract_token` with sudo
103 Then I verify that running `ua disable livepatch` `as non-root` exits `1`86 Then I verify that running `ua disable foobar --format json` `as non-root` exits `1`
104 And stderr matches regexp:87 And stdout is a json matching the `ua_operation` schema
88 And I will see the following on stdout:
105 """89 """
106 This command must be run as root \(try using sudo\).90 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
107 """91 """
108 And I verify that running `ua disable livepatch` `with sudo` exits `1`92 Then I verify that running `ua disable foobar --format json` `with sudo` exits `1`
93 And stdout is a json matching the `ua_operation` schema
109 And I will see the following on stdout:94 And I will see the following on stdout:
110 """95 """
111 Livepatch is not currently enabled96 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
112 See: sudo ua status97 """
98 Then I verify that running `ua disable foobar --format json --assume-yes` `as non-root` exits `1`
99 And stdout is a json matching the `ua_operation` schema
100 And I will see the following on stdout:
101 """
102 {"_schema_version": "0.1", "errors": [{"message": "This command must be run as root (try using sudo).", "message_code": "nonroot-user", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
103 """
104 And I verify that running `ua disable foobar --format json --assume-yes` `with sudo` exits `1`
105 And stdout is a json matching the `ua_operation` schema
106 And I will see the following on stdout:
107 """
108 {"_schema_version": "0.1", "errors": [{"message": "Cannot disable unknown service 'foobar'.\nTry <valid_services>", "message_code": "invalid-service-or-failure", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
113 """109 """
110 And I verify that running `ua disable livepatch --format json --assume-yes` `with sudo` exits `1`
111 And stdout is a json matching the `ua_operation` schema
112 And I will see the following on stdout:
113 """
114 {"_schema_version": "0.1", "errors": [{"message": "Livepatch is not currently enabled\nSee: sudo ua status", "message_code": "service-already-disabled", "service": "livepatch", "type": "service"}], "failed_services": ["livepatch"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
115 """
116 And I verify that running `ua disable esm-infra esm-apps --format json --assume-yes` `with sudo` exits `0`
117 And stdout is a json matching the `ua_operation` schema
118 And I will see the following on stdout:
119 """
120 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-apps", "esm-infra"], "result": "success", "warnings": []}
121 """
122 When I run `ua enable esm-infra` with sudo
123 Then I verify that running `ua disable esm-infra foobar --format json --assume-yes` `with sudo` exits `1`
124 And stdout is a json matching the `ua_operation` schema
125 And I will see the following on stdout:
126 """
127 {"_schema_version": "0.1", "errors": [{"message": "Cannot disable unknown service 'foobar'.\nTry <valid_services>", "message_code": "invalid-service-or-failure", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-infra"], "result": "failure", "warnings": []}
128 """
114129
115 Examples: ubuntu release130 Examples: ubuntu release
116 | release |131 | release | valid_services |
117 | bionic |132 | xenial | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates. |
118 | focal |133 | bionic | cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates. |
119 | xenial |134 | focal | cc-eal, esm-apps, esm-infra, fips, fips-updates, livepatch, ros,\nros-updates, usg. |
120 | hirsute |
121 | impish |
122 | jammy |
123135
124 @series.xenial136 @series.xenial
125 @series.bionic137 @series.bionic
@@ -241,6 +253,31 @@ Feature: Command behaviour when attached to an UA subscription
241 This machine is not attached to a UA subscription.253 This machine is not attached to a UA subscription.
242 """254 """
243 And I verify that running `apt update` `with sudo` exits `0`255 And I verify that running `apt update` `with sudo` exits `0`
256 When I attach `contract_token` with sudo
257 Then I verify that running `ua enable foobar --format json` `as non-root` exits `1`
258 And stdout is a json matching the `ua_operation` schema
259 And I will see the following on stdout:
260 """
261 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
262 """
263 Then I verify that running `ua enable foobar --format json` `with sudo` exits `1`
264 And stdout is a json matching the `ua_operation` schema
265 And I will see the following on stdout:
266 """
267 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
268 """
269 Then I verify that running `ua detach --format json --assume-yes` `as non-root` exits `1`
270 And stdout is a json matching the `ua_operation` schema
271 And I will see the following on stdout:
272 """
273 {"_schema_version": "0.1", "errors": [{"message": "This command must be run as root (try using sudo).", "message_code": "nonroot-user", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
274 """
275 When I run `ua detach --format json --assume-yes` with sudo
276 Then stdout is a json matching the `ua_operation` schema
277 And I will see the following on stdout:
278 """
279 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-apps", "esm-infra"], "result": "success", "warnings": []}
280 """
244281
245 Examples: ubuntu release282 Examples: ubuntu release
246 | release | esm-apps | cc-eal | cis | fips | fips-update | ros | cis_or_usg |283 | release | esm-apps | cc-eal | cis | fips | fips-update | ros | cis_or_usg |
@@ -269,7 +306,6 @@ Feature: Command behaviour when attached to an UA subscription
269 | bionic |306 | bionic |
270 | focal |307 | focal |
271 | xenial |308 | xenial |
272 | hirsute |
273 | impish |309 | impish |
274 | jammy |310 | jammy |
275311
@@ -292,7 +328,6 @@ Feature: Command behaviour when attached to an UA subscription
292 | bionic |328 | bionic |
293 | focal |329 | focal |
294 | xenial |330 | xenial |
295 | hirsute |
296 | impish |331 | impish |
297 | jammy |332 | jammy |
298333
@@ -344,7 +379,6 @@ Feature: Command behaviour when attached to an UA subscription
344 | bionic |379 | bionic |
345 | focal |380 | focal |
346 | xenial |381 | xenial |
347 | hirsute |
348 | impish |382 | impish |
349 | jammy |383 | jammy |
350384
@@ -534,7 +568,6 @@ Feature: Command behaviour when attached to an UA subscription
534 | release | infra-status |568 | release | infra-status |
535 | bionic | enabled |569 | bionic | enabled |
536 | xenial | enabled |570 | xenial | enabled |
537 | hirsute | n/a |
538 | impish | n/a |571 | impish | n/a |
539 | jammy | n/a |572 | jammy | n/a |
540573
@@ -751,7 +784,6 @@ Feature: Command behaviour when attached to an UA subscription
751 | xenial |784 | xenial |
752 | bionic |785 | bionic |
753 | focal |786 | focal |
754 | hirsute |
755 | impish |787 | impish |
756 | jammy |788 | jammy |
757789
@@ -884,14 +916,11 @@ Feature: Command behaviour when attached to an UA subscription
884 """916 """
885 And I run `dpkg-reconfigure ubuntu-advantage-tools` with sudo917 And I run `dpkg-reconfigure ubuntu-advantage-tools` with sudo
886 And I run `apt-get update` with sudo918 And I run `apt-get update` with sudo
887 When I run `ua security-status --format json --beta` as non-root919 When I run `ua security-status --format json` as non-root
888 Then stdout is formatted as `json` and has keys:920 Then stdout is a json matching the `ua_security_status` schema
889 """
890 _schema_version summary packages
891 """
892 And stdout matches regexp:921 And stdout matches regexp:
893 """922 """
894 "_schema_version": "0"923 "_schema_version": "0.1"
895 """924 """
896 And stdout matches regexp:925 And stdout matches regexp:
897 """926 """
@@ -915,13 +944,17 @@ Feature: Command behaviour when attached to an UA subscription
915 """944 """
916 And stdout matches regexp:945 And stdout matches regexp:
917 """946 """
947 "origin": "esm.ubuntu.com"
948 """
949 And stdout matches regexp:
950 """
918 "status": "pending_attach"951 "status": "pending_attach"
919 """952 """
920 When I attach `contract_token` with sudo953 When I attach `contract_token` with sudo
921 And I run `ua security-status --format json --beta` as non-root954 And I run `ua security-status --format json` as non-root
922 Then stdout matches regexp:955 Then stdout matches regexp:
923 """956 """
924 "_schema_version": "0"957 "_schema_version": "0.1"
925 """958 """
926 And stdout matches regexp:959 And stdout matches regexp:
927 """960 """
@@ -939,32 +972,23 @@ Feature: Command behaviour when attached to an UA subscription
939 """972 """
940 "status": "upgrade_available"973 "status": "upgrade_available"
941 """974 """
942 When I run `ua security-status --format yaml --beta` as non-root975 When I run `ua security-status --format yaml` as non-root
943 Then stdout is formatted as `yaml` and has keys:976 Then stdout is a yaml matching the `ua_security_status` schema
944 """
945 _schema_version summary packages
946 """
947 And stdout matches regexp:977 And stdout matches regexp:
948 """978 """
949 _schema_version: '0'979 _schema_version: '0.1'
950 """
951 When I verify that running `ua security-status --format json` `as non-root` exits `2`
952 Then I will see the following on stderr:
953 """
954 usage: security-status [-h] --format {json,yaml} --beta
955 the following arguments are required: --beta
956 """980 """
957 When I verify that running `ua security-status --format unsupported --beta` `as non-root` exits `2`981 When I verify that running `ua security-status --format unsupported` `as non-root` exits `2`
958 Then I will see the following on stderr:982 Then I will see the following on stderr:
959 """983 """
960 usage: security-status [-h] --format {json,yaml} --beta984 usage: security-status [-h] --format {json,yaml}
961 argument --format: invalid choice: 'unsupported' (choose from 'json', 'yaml')985 argument --format: invalid choice: 'unsupported' (choose from 'json', 'yaml')
962 """986 """
963 When I verify that running `ua security-status` `as non-root` exits `2`987 When I verify that running `ua security-status` `as non-root` exits `2`
964 Then I will see the following on stderr:988 Then I will see the following on stderr:
965 """989 """
966 usage: security-status [-h] --format {json,yaml} --beta990 usage: security-status [-h] --format {json,yaml}
967 the following arguments are required: --format, --beta991 the following arguments are required: --format
968 """992 """
969 Examples: ubuntu release993 Examples: ubuntu release
970 | release | package | service |994 | release | package | service |
diff --git a/features/attached_enable.feature b/features/attached_enable.feature
index 2fda0de..90a62cb 100644
--- a/features/attached_enable.feature
+++ b/features/attached_enable.feature
@@ -29,7 +29,6 @@ Feature: Enable command behaviour when attached to an UA subscription
29 | bionic |29 | bionic |
3030
31 @series.focal31 @series.focal
32 @series.hirsute
33 @series.impish32 @series.impish
34 @uses.config.machine_type.lxd.container33 @uses.config.machine_type.lxd.container
35 Scenario Outline: Attached enable Common Criteria service in an ubuntu lxd container34 Scenario Outline: Attached enable Common Criteria service in an ubuntu lxd container
@@ -49,11 +48,80 @@ Feature: Enable command behaviour when attached to an UA subscription
49 Examples: ubuntu release48 Examples: ubuntu release
50 | release | version | full_name |49 | release | version | full_name |
51 | focal | 20.04 LTS | Focal Fossa |50 | focal | 20.04 LTS | Focal Fossa |
52 | hirsute | 21.04 | Hirsute Hippo |
53 | impish | 21.10 | Impish Indri |51 | impish | 21.10 | Impish Indri |
5452
55 @series.xenial53 @series.xenial
56 @series.bionic54 @series.bionic
55 @series.focal
56 @uses.config.machine_type.lxd.container
57 Scenario Outline: Attached enable of different services using json format
58 Given a `<release>` machine with ubuntu-advantage-tools installed
59 When I attach `contract_token` with sudo
60 Then I verify that running `ua enable foobar --format json` `as non-root` exits `1`
61 And stdout is a json matching the `ua_operation` schema
62 And I will see the following on stdout:
63 """
64 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
65 """
66 Then I verify that running `ua enable foobar --format json` `with sudo` exits `1`
67 And stdout is a json matching the `ua_operation` schema
68 And I will see the following on stdout:
69 """
70 {"_schema_version": "0.1", "errors": [{"message": "json formatted response requires --assume-yes flag.", "message_code": "json-format-require-assume-yes", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
71 """
72 Then I verify that running `ua enable foobar --format json --assume-yes` `as non-root` exits `1`
73 And stdout is a json matching the `ua_operation` schema
74 And I will see the following on stdout:
75 """
76 {"_schema_version": "0.1", "errors": [{"message": "This command must be run as root (try using sudo).", "message_code": "nonroot-user", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
77 """
78 And I verify that running `ua enable foobar --format json --assume-yes` `with sudo` exits `1`
79 And stdout is a json matching the `ua_operation` schema
80 And I will see the following on stdout:
81 """
82 {"_schema_version": "0.1", "errors": [{"message": "Cannot enable unknown service 'foobar'.\nTry <valid_services>", "message_code": "invalid-service-or-failure", "service": null, "type": "system"}], "failed_services": ["foobar"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
83 """
84 And I verify that running `ua enable ros foobar --format json --assume-yes` `with sudo` exits `1`
85 And stdout is a json matching the `ua_operation` schema
86 And I will see the following on stdout:
87 """
88 {"_schema_version": "0.1", "errors": [{"message": "Cannot enable unknown service 'foobar, ros'.\nTry <valid_services>", "message_code": "invalid-service-or-failure", "service": null, "type": "system"}], "failed_services": ["foobar", "ros"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
89 """
90 And I verify that running `ua enable esm-infra --format json --assume-yes` `with sudo` exits `1`
91 And stdout is a json matching the `ua_operation` schema
92 Then I will see the following on stdout:
93 """
94 {"_schema_version": "0.1", "errors": [{"message": "UA Infra: ESM is already enabled.\nSee: sudo ua status", "message_code": "service-already-enabled", "service": "esm-infra", "type": "service"}], "failed_services": ["esm-infra"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
95 """
96 When I run `ua disable esm-infra` with sudo
97 And I run `ua enable esm-infra --format json --assume-yes` with sudo
98 Then stdout is a json matching the `ua_operation` schema
99 And I will see the following on stdout:
100 """
101 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-infra"], "result": "success", "warnings": []}
102 """
103 When I run `ua disable esm-infra` with sudo
104 And I verify that running `ua enable esm-infra foobar --format json --assume-yes` `with sudo` exits `1`
105 Then stdout is a json matching the `ua_operation` schema
106 And I will see the following on stdout:
107 """
108 {"_schema_version": "0.1", "errors": [{"message": "Cannot enable unknown service 'foobar'.\nTry <valid_services>", "message_code": "invalid-service-or-failure", "service": null, "type": "system"}], "failed_services": ["foobar"], "needs_reboot": false, "processed_services": ["esm-infra"], "result": "failure", "warnings": []}
109 """
110 When I run `ua disable esm-infra esm-apps` with sudo
111 And I run `ua enable esm-infra esm-apps --beta --format json --assume-yes` with sudo
112 Then stdout is a json matching the `ua_operation` schema
113 And I will see the following on stdout:
114 """
115 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["esm-apps", "esm-infra"], "result": "success", "warnings": []}
116 """
117
118 Examples: ubuntu release
119 | release | valid_services |
120 | xenial | cc-eal, cis, esm-infra, fips, fips-updates, livepatch. |
121 | bionic | cc-eal, cis, esm-infra, fips, fips-updates, livepatch. |
122 | focal | cc-eal, esm-infra, fips, fips-updates, livepatch, usg. |
123
124 @series.lts
57 @uses.config.machine_type.lxd.container125 @uses.config.machine_type.lxd.container
58 Scenario Outline: Attached enable of a service in a ubuntu machine126 Scenario Outline: Attached enable of a service in a ubuntu machine
59 Given a `<release>` machine with ubuntu-advantage-tools installed127 Given a `<release>` machine with ubuntu-advantage-tools installed
@@ -84,7 +152,7 @@ Feature: Enable command behaviour when attached to an UA subscription
84 Try cc-eal, cis, esm-infra, fips, fips-updates, livepatch.152 Try cc-eal, cis, esm-infra, fips, fips-updates, livepatch.
85 """153 """
86 And I verify that running `ua enable esm-infra` `with sudo` exits `1`154 And I verify that running `ua enable esm-infra` `with sudo` exits `1`
87 Then I will see the following on stdout:155 And I will see the following on stdout:
88 """156 """
89 One moment, checking your subscription first157 One moment, checking your subscription first
90 UA Infra: ESM is already enabled.158 UA Infra: ESM is already enabled.
@@ -176,25 +244,12 @@ Feature: Enable command behaviour when attached to an UA subscription
176 One moment, checking your subscription first244 One moment, checking your subscription first
177 Cannot install Livepatch on a container.245 Cannot install Livepatch on a container.
178 """246 """
179 And I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
180 And I will see the following on stdout:
181 """
182 One moment, checking your subscription first
183 Cannot install FIPS on a container.
184 """
185 And I verify that running `ua enable fips-updates --assume-yes` `with sudo` exits `1`
186 And I will see the following on stdout:
187 """
188 One moment, checking your subscription first
189 Cannot install FIPS Updates on a container.
190 """
191247
192 Examples: Un-supported services in containers248 Examples: Un-supported services in containers
193 | release |249 | release |
194 | bionic |250 | bionic |
195 | focal |251 | focal |
196 | xenial |252 | xenial |
197 | hirsute |
198 | impish |253 | impish |
199 | jammy |254 | jammy |
200255
@@ -495,7 +550,7 @@ Feature: Enable command behaviour when attached to an UA subscription
495 """550 """
496 When I run `ua disable livepatch` with sudo551 When I run `ua disable livepatch` with sudo
497 Then I verify that running `canonical-livepatch status` `with sudo` exits `1`552 Then I verify that running `canonical-livepatch status` `with sudo` exits `1`
498 And stdout matches regexp:553 And stderr matches regexp:
499 """554 """
500 Machine is not enabled. Please run 'sudo canonical-livepatch enable' with the555 Machine is not enabled. Please run 'sudo canonical-livepatch enable' with the
501 token obtained from https://ubuntu.com/livepatch.556 token obtained from https://ubuntu.com/livepatch.
@@ -581,6 +636,11 @@ Feature: Enable command behaviour when attached to an UA subscription
581 One moment, checking your subscription first636 One moment, checking your subscription first
582 Cannot enable Livepatch when FIPS is enabled.637 Cannot enable Livepatch when FIPS is enabled.
583 """638 """
639 Then I verify that running `ua enable livepatch --format json --assume-yes` `with sudo` exits `1`
640 And I will see the following on stdout
641 """
642 {"_schema_version": "0.1", "errors": [{"message": "Cannot enable Livepatch when FIPS is enabled.", "message_code": "livepatch-error-when-fips-enabled", "service": "livepatch", "type": "service"}], "failed_services": ["livepatch"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
643 """
584644
585 @series.bionic645 @series.bionic
586 @uses.config.machine_type.lxd.vm646 @uses.config.machine_type.lxd.vm
@@ -603,11 +663,14 @@ Feature: Enable command behaviour when attached to an UA subscription
603 And I will see the following on stdout663 And I will see the following on stdout
604 """664 """
605 One moment, checking your subscription first665 One moment, checking your subscription first
606 """
607 And I will see the following on stderr
608 """
609 Cannot enable FIPS when Livepatch is enabled.666 Cannot enable FIPS when Livepatch is enabled.
610 """667 """
668 Then I verify that running `ua enable fips --assume-yes --format json` `with sudo` exits `1`
669 And stdout is a json matching the `ua_operation` schema
670 And I will see the following on stdout:
671 """
672 {"_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": []}
673 """
611674
612 @slow675 @slow
613 @series.xenial676 @series.xenial
@@ -688,7 +751,8 @@ Feature: Enable command behaviour when attached to an UA subscription
688 | bionic |751 | bionic |
689 | xenial |752 | xenial |
690753
691 @series.lts754 @series.xenial
755 @series.bionic
692 @uses.config.contract_token756 @uses.config.contract_token
693 @uses.config.machine_type.lxd.container757 @uses.config.machine_type.lxd.container
694 Scenario Outline: Attached enable ros on a machine758 Scenario Outline: Attached enable ros on a machine
@@ -741,7 +805,6 @@ Feature: Enable command behaviour when attached to an UA subscription
741 ROS ESM Security Updates cannot be enabled with UA Apps: ESM disabled.805 ROS ESM Security Updates cannot be enabled with UA Apps: ESM disabled.
742 Enable UA Apps: ESM and proceed to enable ROS ESM Security Updates\? \(y\/N\) Cannot enable ROS ESM Security Updates when UA Apps: ESM is disabled.806 Enable UA Apps: ESM and proceed to enable ROS ESM Security Updates\? \(y\/N\) Cannot enable ROS ESM Security Updates when UA Apps: ESM is disabled.
743 """807 """
744
745 When I run `ua enable ros --beta` `with sudo` and stdin `y`808 When I run `ua enable ros --beta` `with sudo` and stdin `y`
746 Then stdout matches regexp809 Then stdout matches regexp
747 """810 """
diff --git a/features/attached_status.feature b/features/attached_status.feature
index 78483ed..59406e7 100644
--- a/features/attached_status.feature
+++ b/features/attached_status.feature
@@ -7,25 +7,14 @@ Feature: Attached status
7 Given a `<release>` machine with ubuntu-advantage-tools installed7 Given a `<release>` machine with ubuntu-advantage-tools installed
8 When I attach `contract_token` with sudo8 When I attach `contract_token` with sudo
9 And I run `ua status --format json` as non-root9 And I run `ua status --format json` as non-root
10 Then stdout is formatted as `json` and has keys:10 Then stdout is a json matching the `ua_status` schema
11 """
12 _doc _schema_version account attached config config_path contract effective
13 environment_vars execution_details execution_status expires machine_id notices
14 services version simulated
15 """
16 When I run `ua status --format yaml` as non-root11 When I run `ua status --format yaml` as non-root
17 Then stdout is formatted as `yaml` and has keys:12 Then stdout is a yaml matching the `ua_status` schema
18 """
19 _doc _schema_version account attached config config_path contract effective
20 environment_vars execution_details execution_status expires machine_id notices
21 services version simulated
22 """
2313
24 Examples: ubuntu release14 Examples: ubuntu release
25 | release |15 | release |
26 | bionic |16 | bionic |
27 | focal |17 | focal |
28 | xenial |18 | xenial |
29 | hirsute |
30 | impish |19 | impish |
31 | jammy |20 | jammy |
diff --git a/features/aws-ids.yaml b/features/aws-ids.yaml
index e0198a7..f2376ad 100644
--- a/features/aws-ids.yaml
+++ b/features/aws-ids.yaml
@@ -1,3 +1,5 @@
1bionic: ami-094e9c95623db463c1bionic: ami-0419d66039473da9d
2focal: ami-0193aa0a9df84a08b2bionic-fips: ami-03b75f613f80bcff1
3xenial: ami-06e94647aeaed1e5c3focal: ami-0489b8bdbbf3a3b32
4xenial: ami-011bcfe2bea365b6a
5xenial-fips: ami-077e4c339a098fc9f
diff --git a/features/azure-ids.yaml b/features/azure-ids.yaml
index 4d43f80..735223f 100644
--- a/features/azure-ids.yaml
+++ b/features/azure-ids.yaml
@@ -1,3 +1,5 @@
1bionic: "Canonical:0001-com-ubuntu-pro-bionic:pro-18_04-lts"1bionic: "Canonical:0001-com-ubuntu-pro-bionic:pro-18_04-lts"
2focal: "Canonical:0001-com-ubuntu-pro-focal:pro-20_04-lts"2focal: "Canonical:0001-com-ubuntu-pro-focal:pro-20_04-lts"
3xenial: "Canonical:0001-com-ubuntu-pro-xenial:pro-16_04-lts"3xenial: "Canonical:0001-com-ubuntu-pro-xenial:pro-16_04-lts"
4bionic-fips: "Canonical:0001-com-ubuntu-pro-bionic-fips:pro-fips-18_04"
5xenial-fips: "Canonical:0001-com-ubuntu-pro-xenial-fips:pro-fips-16_04-private"
diff --git a/features/cloud.py b/features/cloud.py
index 4938374..b0df204 100644
--- a/features/cloud.py
+++ b/features/cloud.py
@@ -218,7 +218,10 @@ class Cloud:
218 if "pro" in self.machine_type:218 if "pro" in self.machine_type:
219 with open(self.pro_ids_path, "r") as stream:219 with open(self.pro_ids_path, "r") as stream:
220 pro_ids = yaml.safe_load(stream.read())220 pro_ids = yaml.safe_load(stream.read())
221 image_name = pro_ids[series]221 if "fips" in self.machine_type:
222 image_name = pro_ids[series + "-fips"]
223 else:
224 image_name = pro_ids[series]
222 else:225 else:
223 image_name = self.api.daily_image(release=series)226 image_name = self.api.daily_image(release=series)
224227
@@ -539,7 +542,7 @@ class Azure(Cloud):
539 List of ports to open for network ingress to the instance542 List of ports to open for network ingress to the instance
540543
541 :returns:544 :returns:
542 An AWS cloud provider instance545 An Azure cloud provider instance
543 """546 """
544 if not image_name:547 if not image_name:
545 image_name = self.locate_image_name(series)548 image_name = self.locate_image_name(series)
@@ -654,7 +657,7 @@ class GCP(Cloud):
654 List of ports to open for network ingress to the instance657 List of ports to open for network ingress to the instance
655658
656 :returns:659 :returns:
657 An AWS cloud provider instance660 An GCP cloud provider instance
658 """661 """
659 if not image_name:662 if not image_name:
660 image_name = self.locate_image_name(series)663 image_name = self.locate_image_name(series)
diff --git a/features/detached_auto_attach.feature b/features/detached_auto_attach.feature
661new file mode 100644664new file mode 100644
index 0000000..463400a
--- /dev/null
+++ b/features/detached_auto_attach.feature
@@ -0,0 +1,32 @@
1@uses.config.contract_token
2Feature: Attached cloud does not detach when auto-attaching after manually attaching
3
4 @series.all
5 @uses.config.machine_type.aws.generic
6 @uses.config.machine_type.azure.generic
7 @uses.config.machine_type.gcp.generic
8 Scenario Outline: No detaching on manually attached machine on all clouds
9 Given a `<release>` machine with ubuntu-advantage-tools installed
10 When I attach `contract_token` with sudo
11 And I run `ua refresh` with sudo
12 Then I will see the following on stdout:
13 """
14 Successfully processed your ua configuration.
15 Successfully refreshed your subscription.
16 """
17 When I run `ua auto-attach` with sudo
18 Then stderr matches regexp:
19 """
20 Skipping attach: Instance '[0-9a-z\-]+' is already attached.
21 """
22 When I run `ua status` with sudo
23 Then stdout matches regexp:
24 """
25 esm-infra +yes +<esm-service> +UA Infra: Extended Security Maintenance \(ESM\)
26 """
27
28 Examples: ubuntu release
29 | release | esm-service |
30 | bionic | enabled |
31 | focal | enabled |
32 | xenial | enabled |
diff --git a/features/enable_fips_container.feature b/features/enable_fips_container.feature
0new file mode 10064433new file mode 100644
index 0000000..8519480
--- /dev/null
+++ b/features/enable_fips_container.feature
@@ -0,0 +1,144 @@
1
2@uses.config.contract_token
3Feature: FIPS enablement in lxd containers
4
5 @series.xenial
6 @series.bionic
7 @series.focal
8 @uses.config.machine_type.lxd.container
9 Scenario Outline: Attached enable of FIPS in an ubuntu lxd container
10 Given a `<release>` machine with ubuntu-advantage-tools installed
11 When I attach `contract_token` with sudo
12 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 openssl <libssl> libgcrypt20` with sudo, retrying exit [100]
13 And I run `ua enable fips<updates>` `with sudo` and stdin `y`
14 Then stdout matches regexp:
15 """
16 Warning: Enabling <fips-name> in a container.
17 This will install the FIPS packages but not the kernel.
18 This container must run on a host with <fips-name> enabled to be
19 compliant.
20 Warning: This action can take some time and cannot be undone.
21 """
22 And stdout matches regexp:
23 """
24 Updating package lists
25 Installing <fips-name> packages
26 <fips-name> enabled
27 A reboot is required to complete install.
28 Please run `apt upgrade` to ensure all FIPS packages are updated to the correct
29 version.
30 """
31 When I run `ua status --all` with sudo
32 Then stdout matches regexp:
33 """
34 fips<updates> +yes enabled
35 """
36 And stdout matches regexp:
37 """
38 FIPS support requires system reboot to complete configuration
39 """
40 And I verify that running `apt update` `with sudo` exits `0`
41 And I verify that `openssh-server` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
42 And I verify that `openssh-client` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
43 And I verify that `strongswan` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
44 And I verify that `strongswan-hmac` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
45 And I verify that `openssl` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
46 And I verify that `<libssl>` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
47 And I verify that `<libssl>-hmac` is installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
48 And I verify that `<additional-fips-packages>` are installed from apt source `https://esm.ubuntu.com/fips<updates>/ubuntu <release><updates>/main`
49 When I reboot the `<release>` machine
50 When I run `ua status --all` with sudo
51 Then stdout does not match regexp:
52 """
53 FIPS support requires system reboot to complete configuration
54 """
55 When I run `ua disable fips<updates>` `with sudo` and stdin `y`
56 Then stdout matches regexp:
57 """
58 This will disable the FIPS entitlement but the FIPS packages will remain installed.
59 """
60 And stdout matches regexp:
61 """
62 Updating package lists
63 """
64 And stdout does not match regexp:
65 """
66 A reboot is required to complete disable operation
67 """
68 When I run `ua status --all` with sudo
69 Then stdout matches regexp:
70 """
71 fips<updates> +yes disabled
72 """
73 Then stdout does not match regexp:
74 """
75 Disabling FIPS requires system reboot to complete operation
76 """
77 When I run `apt-cache policy ubuntu-fips` as non-root
78 Then stdout does not match regexp:
79 """
80 .*Installed: \(none\)
81 """
82 Then I verify that `openssh-server` installed version matches regexp `fips`
83 And I verify that `openssh-client` installed version matches regexp `fips`
84 And I verify that `strongswan` installed version matches regexp `fips`
85 And I verify that `strongswan-hmac` installed version matches regexp `fips`
86 And I verify that `openssl` installed version matches regexp `fips`
87 And I verify that `<libssl>` installed version matches regexp `fips`
88 And I verify that `<libssl>-hmac` installed version matches regexp `fips`
89 And I verify that packages `<additional-fips-packages>` installed versions match regexp `fips`
90
91 Examples: ubuntu release
92 | release | fips-name | updates | libssl | additional-fips-packages |
93 | xenial | FIPS | | libssl1.0.0 | openssh-server-hmac openssh-client-hmac |
94 | xenial | FIPS Updates | -updates | libssl1.0.0 | openssh-server-hmac openssh-client-hmac |
95 | bionic | FIPS | | libssl1.1 | openssh-server-hmac openssh-client-hmac libgcrypt20 libgcrypt20-hmac |
96 | bionic | FIPS Updates | -updates | libssl1.1 | openssh-server-hmac openssh-client-hmac libgcrypt20 libgcrypt20-hmac |
97 | focal | FIPS | | libssl1.1 | libgcrypt20 libgcrypt20-hmac |
98 | focal | FIPS Updates | -updates | libssl1.1 | libgcrypt20 libgcrypt20-hmac |
99
100 @series.xenial
101 @series.bionic
102 @series.focal
103 @uses.config.machine_type.lxd.container
104 Scenario Outline: Try to enable FIPS after FIPS Updates in a lxd container
105 Given a `<release>` machine with ubuntu-advantage-tools installed
106 When I attach `contract_token` with sudo
107 When I run `ua status --all` with sudo
108 Then stdout matches regexp:
109 """
110 fips-updates +yes +disabled
111 """
112 And stdout matches regexp:
113 """
114 fips +yes +disabled
115 """
116 When I run `ua enable fips-updates --assume-yes` with sudo
117 When I run `ua status --all` with sudo
118 Then stdout matches regexp:
119 """
120 fips-updates +yes +enabled
121 """
122 And stdout matches regexp:
123 """
124 fips +yes +n/a
125 """
126 When I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
127 Then stdout matches regexp:
128 """
129 Cannot enable FIPS when FIPS Updates is enabled.
130 """
131 When I run `ua status --all` with sudo
132 Then stdout matches regexp:
133 """
134 fips-updates +yes +enabled
135 """
136 And stdout matches regexp:
137 """
138 fips +yes +n/a
139 """
140 Examples: ubuntu release
141 | release |
142 | xenial |
143 | bionic |
144 | focal |
diff --git a/features/enable_fips_vm.feature b/features/enable_fips_vm.feature
index b401bf2..a055354 100644
--- a/features/enable_fips_vm.feature
+++ b/features/enable_fips_vm.feature
@@ -8,7 +8,12 @@ Feature: FIPS enablement in lxd VMs
8 Scenario Outline: Attached enable of FIPS in an ubuntu lxd vm8 Scenario Outline: Attached enable of FIPS in an ubuntu lxd vm
9 Given a `<release>` machine with ubuntu-advantage-tools installed9 Given a `<release>` machine with ubuntu-advantage-tools installed
10 When I attach `contract_token` with sudo10 When I attach `contract_token` with sudo
11 And I run `ua disable livepatch` with sudo11 When I run `ua status --format json` with sudo
12 Then stdout contains substring
13 """
14 {"available": "yes", "blocked_by": [{"name": "livepatch", "reason": "Livepatch cannot be enabled while running the official FIPS certified kernel. If you would like a FIPS compliant kernel with additional bug fixes and security updates, you can use the FIPS Updates service with Livepatch.", "reason_code": "livepatch-invalidates-fips"}], "description": "NIST-certified core packages", "description_override": null, "entitled": "yes", "name": "fips", "status": "disabled", "status_details": "FIPS is not configured"}
15 """
16 When I run `ua disable livepatch` with sudo
12 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]17 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]
13 And I run `apt-mark hold openssh-client openssh-server strongswan` with sudo18 And I run `apt-mark hold openssh-client openssh-server strongswan` with sudo
14 And I run `ua enable <fips-service>` `with sudo` and stdin `y`19 And I run `ua enable <fips-service>` `with sudo` and stdin `y`
@@ -40,6 +45,12 @@ Feature: FIPS enablement in lxd VMs
40 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`45 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
41 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`46 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
42 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`47 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
48 When I run `ua status --format json` with sudo
49 Then stdout contains substring:
50 """
51 {"available": "yes", "blocked_by": [{"name": "fips", "reason": "Livepatch cannot be enabled while running the official FIPS certified kernel. If you would like a FIPS compliant kernel with additional bug fixes and security updates, you can use the FIPS Updates service with Livepatch.", "reason_code": "livepatch-invalidates-fips"}], "description": "Canonical Livepatch service", "description_override": null, "entitled": "yes", "name": "livepatch", "status": "n/a", "status_details": "Cannot enable Livepatch when FIPS is enabled."}
52 """
53
43 When I reboot the `<release>` machine54 When I reboot the `<release>` machine
44 And I run `uname -r` as non-root55 And I run `uname -r` as non-root
45 Then stdout matches regexp:56 Then stdout matches regexp:
@@ -99,6 +110,24 @@ Feature: FIPS enablement in lxd VMs
99 """110 """
100 Disabling FIPS requires system reboot to complete operation111 Disabling FIPS requires system reboot to complete operation
101 """112 """
113 When I run `ua enable <fips-service> --assume-yes --format json --assume-yes` with sudo
114 Then stdout is a json matching the `ua_operation` schema
115 And I will see the following on stdout:
116 """
117 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": true, "processed_services": ["<fips-service>"], "result": "success", "warnings": []}
118 """
119 When I reboot the `<release>` machine
120 And I run `ua disable <fips-service> --assume-yes --format json` with sudo
121 Then stdout is a json matching the `ua_operation` schema
122 And I will see the following on stdout:
123 """
124 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": true, "processed_services": ["<fips-service>"], "result": "success", "warnings": []}
125 """
126 When I run `ua status --all` with sudo
127 Then stdout matches regexp:
128 """
129 <fips-service> +yes disabled
130 """
102131
103 Examples: ubuntu release132 Examples: ubuntu release
104 | release | fips-name | fips-service |fips-apt-source |133 | release | fips-name | fips-service |fips-apt-source |
@@ -139,6 +168,12 @@ Feature: FIPS enablement in lxd VMs
139 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`168 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
140 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`169 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
141 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`170 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
171 When I run `ua status --format json` with sudo
172 Then stdout contains substring:
173 """
174 {"available": "yes", "blocked_by": [{"name": "fips-updates", "reason": "FIPS cannot be enabled if FIPS Updates has ever been enabled because FIPS Updates installs security patches that aren't officially certified.", "reason_code": "fips-updates-invalidates-fips"}], "description": "NIST-certified core packages", "description_override": null, "entitled": "yes", "name": "fips", "status": "n/a", "status_details": "Cannot enable FIPS when FIPS Updates is enabled."}
175 """
176
142 When I reboot the `<release>` machine177 When I reboot the `<release>` machine
143 And I run `uname -r` as non-root178 And I run `uname -r` as non-root
144 Then stdout matches regexp:179 Then stdout matches regexp:
@@ -207,6 +242,30 @@ Feature: FIPS enablement in lxd VMs
207 """242 """
208 livepatch +yes +enabled243 livepatch +yes +enabled
209 """244 """
245 When I run `ua status --format json` with sudo
246 Then stdout contains substring:
247 """
248 {"available": "yes", "blocked_by": [{"name": "livepatch", "reason": "Livepatch cannot be enabled while running the official FIPS certified kernel. If you would like a FIPS compliant kernel with additional bug fixes and security updates, you can use the FIPS Updates service with Livepatch.", "reason_code": "livepatch-invalidates-fips"}, {"name": "fips-updates", "reason": "FIPS cannot be enabled if FIPS Updates has ever been enabled because FIPS Updates installs security patches that aren't officially certified.", "reason_code": "fips-updates-invalidates-fips"}], "description": "NIST-certified core packages", "description_override": null, "entitled": "yes", "name": "fips", "status": "n/a", "status_details": "Cannot enable FIPS when FIPS Updates is enabled."}
249 """
250 When I run `ua disable <fips-service> --assume-yes` with sudo
251 And I run `ua enable <fips-service> --assume-yes --format json --assume-yes` with sudo
252 Then stdout is a json matching the `ua_operation` schema
253 And I will see the following on stdout:
254 """
255 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": true, "processed_services": ["<fips-service>"], "result": "success", "warnings": []}
256 """
257 When I reboot the `<release>` machine
258 And I run `ua disable <fips-service> --assume-yes --format json` with sudo
259 Then stdout is a json matching the `ua_operation` schema
260 And I will see the following on stdout:
261 """
262 {"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": true, "processed_services": ["<fips-service>"], "result": "success", "warnings": []}
263 """
264 When I run `ua status --all` with sudo
265 Then stdout matches regexp:
266 """
267 <fips-service> +yes disabled
268 """
210269
211 Examples: ubuntu release270 Examples: ubuntu release
212 | release | fips-name | fips-service |fips-apt-source |271 | release | fips-name | fips-service |fips-apt-source |
diff --git a/features/install_uninstall.feature b/features/install_uninstall.feature
index 66426c8..078b1b5 100644
--- a/features/install_uninstall.feature
+++ b/features/install_uninstall.feature
@@ -12,7 +12,6 @@ Feature: UA Install and Uninstall related tests
12 | xenial |12 | xenial |
13 | bionic |13 | bionic |
14 | focal |14 | focal |
15 | hirsute |
16 | impish |15 | impish |
17 | jammy |16 | jammy |
1817
diff --git a/features/license_check.feature b/features/license_check.feature
index 01d4425..b368360 100644
--- a/features/license_check.feature
+++ b/features/license_check.feature
@@ -38,8 +38,9 @@ Feature: License check timer only runs in environments where necessary
38 | xenial |38 | xenial |
39 | bionic |39 | bionic |
40 | focal |40 | focal |
41 | jammy |
4142
42 @series.hirsute43 @series.impish
43 @uses.config.contract_token44 @uses.config.contract_token
44 @uses.config.machine_type.gcp.generic45 @uses.config.machine_type.gcp.generic
45 Scenario Outline: license_check is disabled gcp generic non lts46 Scenario Outline: license_check is disabled gcp generic non lts
@@ -59,7 +60,7 @@ Feature: License check timer only runs in environments where necessary
59 Then I verify the `ua-license-check` systemd timer is disabled60 Then I verify the `ua-license-check` systemd timer is disabled
60 Examples: version61 Examples: version
61 | release |62 | release |
62 | hirsute |63 | impish |
6364
64 @series.all65 @series.all
65 @uses.config.contract_token66 @uses.config.contract_token
@@ -91,7 +92,6 @@ Feature: License check timer only runs in environments where necessary
91 | xenial |92 | xenial |
92 | bionic |93 | bionic |
93 | focal |94 | focal |
94 | hirsute |
95 | impish |95 | impish |
96 | jammy |96 | jammy |
9797
diff --git a/features/proxy_config.feature b/features/proxy_config.feature
index 7ee5c5a..30f03cf 100644
--- a/features/proxy_config.feature
+++ b/features/proxy_config.feature
@@ -24,12 +24,23 @@ Feature: Proxy configuration
24 https_proxy: http://<ci-proxy-ip>:312824 https_proxy: http://<ci-proxy-ip>:3128
25 """25 """
26 And I verify `/var/log/squid/access.log` is empty on `proxy` machine26 And I verify `/var/log/squid/access.log` is empty on `proxy` machine
27 And I attach `contract_token` with sudo27 # We need this for the route command
28 And I run `apt-get install net-tools` with sudo
29 # We will guarantee that the machine will only use the proxy when
30 # running the ua commands
31 And I run `route del default` with sudo
32 And I attach `contract_token` with sudo and options `--no-auto-enable`
28 And I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine33 And I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
29 Then stdout matches regexp:34 Then stdout matches regexp:
30 """35 """
31 .*CONNECT contracts.canonical.com.*36 .*CONNECT contracts.canonical.com.*
32 """37 """
38 When I run `ua status` with sudo
39 # Just to verify that the machine is attached
40 Then stdout matches regexp:
41 """
42 esm-infra +yes +disabled +UA Infra: Extended Security Maintenance \(ESM\)
43 """
33 When I run `truncate -s 0 /var/log/squid/access.log` `with sudo` on the `proxy` machine44 When I run `truncate -s 0 /var/log/squid/access.log` `with sudo` on the `proxy` machine
34 When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:45 When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
35 """46 """
@@ -139,8 +150,12 @@ Feature: Proxy configuration
139 And I run `systemctl restart squid.service` `with sudo` on the `proxy` machine150 And I run `systemctl restart squid.service` `with sudo` on the `proxy` machine
140 And I run `ua config set http_proxy=http://<ci-proxy-ip>:3128` with sudo151 And I run `ua config set http_proxy=http://<ci-proxy-ip>:3128` with sudo
141 And I run `ua config set https_proxy=http://<ci-proxy-ip>:3128` with sudo152 And I run `ua config set https_proxy=http://<ci-proxy-ip>:3128` with sudo
142 And I verify `/var/log/squid/access.log` is empty on `proxy` machine153 And I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
143 And I attach `contract_token` with sudo154 Then stdout matches regexp:
155 """
156 .*CONNECT api.snapcraft.io.*
157 """
158 When I attach `contract_token` with sudo
144 Then stdout matches regexp:159 Then stdout matches regexp:
145 """160 """
146 Setting snap proxy161 Setting snap proxy
@@ -296,8 +311,12 @@ Feature: Proxy configuration
296 And I run `systemctl restart squid.service` `with sudo` on the `proxy` machine311 And I run `systemctl restart squid.service` `with sudo` on the `proxy` machine
297 And I run `ua config set http_proxy=http://someuser:somepassword@<ci-proxy-ip>:3128` with sudo312 And I run `ua config set http_proxy=http://someuser:somepassword@<ci-proxy-ip>:3128` with sudo
298 And I run `ua config set https_proxy=http://someuser:somepassword@<ci-proxy-ip>:3128` with sudo313 And I run `ua config set https_proxy=http://someuser:somepassword@<ci-proxy-ip>:3128` with sudo
299 And I verify `/var/log/squid/access.log` is empty on `proxy` machine314 And I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
300 And I attach `contract_token` with sudo315 Then stdout matches regexp:
316 """
317 .*CONNECT api.snapcraft.io:443.*
318 """
319 When I attach `contract_token` with sudo
301 Then stdout matches regexp:320 Then stdout matches regexp:
302 """321 """
303 Setting snap proxy322 Setting snap proxy
diff --git a/features/schemas/ua_operation.json b/features/schemas/ua_operation.json
304new file mode 100644323new file mode 100644
index 0000000..2afb7c8
--- /dev/null
+++ b/features/schemas/ua_operation.json
@@ -0,0 +1,76 @@
1{
2 "type": "object",
3 "properties": {
4 "_schema_version": {
5 "type": "string",
6 "const": "0.1"
7 },
8 "result": {
9 "type": "string",
10 "enum": ["success", "failure"]
11 },
12 "errors": {
13 "type": "array",
14 "items": {
15 "type": "object",
16 "required": [ "message", "service", "type" ],
17 "properties": {
18 "message": {
19 "type": "string"
20 },
21 "message_code": {
22 "type": ["null", "string"]
23 },
24 "service": {
25 "type": ["null", "string"]
26 }
27 },
28 "patternProperties": {
29 "^type$": {
30 "type": "string",
31 "enum": ["service", "system"]
32 }
33 }
34 }
35 },
36 "warnings": {
37 "type": "array",
38 "items": {
39 "type": "object",
40 "required": [ "message", "service", "type" ],
41 "properties": {
42 "message": {
43 "type": "string"
44 },
45 "message_code": {
46 "type": ["null", "string"]
47 },
48 "service": {
49 "type": ["null", "string"]
50 }
51 },
52 "patternProperties": {
53 "^type$": {
54 "type": "string",
55 "enum": ["service", "system"]
56 }
57 }
58 }
59 },
60 "failed_services": {
61 "type": "array",
62 "items": {
63 "type": "string"
64 }
65 },
66 "processed_services": {
67 "type": "array",
68 "items": {
69 "type": "string"
70 }
71 },
72 "needs_reboot": {
73 "type": "boolean"
74 }
75 }
76}
diff --git a/features/schemas/ua_security_status.json b/features/schemas/ua_security_status.json
0new file mode 10064477new file mode 100644
index 0000000..e099976
--- /dev/null
+++ b/features/schemas/ua_security_status.json
@@ -0,0 +1,81 @@
1{
2 "type": "object",
3 "properties": {
4 "_schema_version": {
5 "type": "string",
6 "const": "0.1"
7 },
8 "summary": {
9 "type": "object",
10 "properties": {
11 "ua": {
12 "type": "object",
13 "properties": {
14 "attached": {
15 "type": "boolean"
16 },
17 "enabled_services": {
18 "type": "array",
19 "items": {
20 "type": "string"
21 }
22 },
23 "entitled_services": {
24 "type": "array",
25 "items": {
26 "type": "string"
27 }
28 }
29 }
30 },
31 "num_installed_packages": {
32 "type": "integer"
33 },
34 "num_esm_infra_packages": {
35 "type": "integer"
36 },
37 "num_esm_apps_packages": {
38 "type": "integer"
39 },
40 "num_esm_infra_updates": {
41 "type": "integer"
42 },
43 "num_esm_apps_updates": {
44 "type": "integer"
45 },
46 "num_standard_security_updates": {
47 "type": "integer"
48 }
49 }
50 },
51 "packages": {
52 "type": "array",
53 "items": {
54 "type": "object",
55 "properties": {
56 "package": {
57 "type": "string"
58 },
59 "version": {
60 "type": "string"
61 },
62 "service_name": {
63 "type": "string"
64 },
65 "origin": {
66 "type": "string"
67 },
68 "status": {
69 "type": "string",
70 "enum": [
71 "upgrade_available",
72 "pending_attach",
73 "pending_enable",
74 "upgrade_unavailable"
75 ]
76 }
77 }
78 }
79 }
80 }
81}
diff --git a/features/schemas/ua_status.json b/features/schemas/ua_status.json
0new file mode 10064482new file mode 100644
index 0000000..ae9fd69
--- /dev/null
+++ b/features/schemas/ua_status.json
@@ -0,0 +1,247 @@
1{
2 "type": "object",
3 "properties": {
4 "_doc": {
5 "type": "string"
6 },
7 "_schema_version": {
8 "type": "string",
9 "const": "0.1"
10 },
11 "version": {
12 "type": "string"
13 },
14 "result": {
15 "type": "string",
16 "enum": ["success", "failure"]
17 },
18 "errors": {
19 "type": "array",
20 "items": {
21 "type": "object",
22 "required": [ "message", "service", "type" ],
23 "properties": {
24 "message": {
25 "type": "string"
26 },
27 "service": {
28 "type": ["null", "string"]
29 }
30 },
31 "patternProperties": {
32 "^type$": {
33 "type": "string",
34 "enum": ["service", "system"]
35 }
36 }
37 }
38 },
39 "warnings": {
40 "type": "array",
41 "items": {
42 "type": "object",
43 "required": [ "message", "service", "type" ],
44 "properties": {
45 "message": {
46 "type": "string"
47 },
48 "service": {
49 "type": ["null", "string"]
50 }
51 },
52 "patternProperties": {
53 "^type$": {
54 "type": "string",
55 "enum": ["service", "system"]
56 }
57 }
58 }
59 },
60 "attached": {
61 "type": "boolean"
62 },
63 "machine_id": {
64 "type": ["null", "string"]
65 },
66 "effective": {
67 "type": ["null", "string"]
68 },
69 "expires": {
70 "type": ["null", "string"]
71 },
72 "execution_status": {
73 "type": "string",
74 "enum": ["active", "inactive", "reboot-required"]
75 },
76 "execution_details": {
77 "type": "string"
78 },
79 "simulated": {
80 "type": "boolean"
81 },
82 "services": {
83 "type": "array",
84 "items": {
85 "type": "object",
86 "properties": {
87 "name": {
88 "type": "string"
89 },
90 "description": {
91 "type": "string"
92 },
93 "available": {
94 "type": "string",
95 "enum": ["yes", "no"]
96 },
97 "entitled": {
98 "type": "string",
99 "enum": ["yes", "no"]
100 },
101 "status": {
102 "type": "string",
103 "enum": ["enabled", "disabled", "n/a"]
104 },
105 "status_details": {
106 "type": "string"
107 },
108 "description_override": {
109 "type": ["null", "string"]
110 },
111 "blocked_by": {
112 "type": "array",
113 "items": {
114 "type": "object",
115 "properties": {
116 "name": {
117 "type": "string"
118 },
119 "reason": {
120 "type": "string"
121 },
122 "reason_code": {
123 "type": "string"
124 }
125 }
126 }
127 }
128 }
129 }
130 },
131 "notices": {
132 "type": "array",
133 "items": {
134 "type": "string"
135 }
136 },
137 "config_path": {
138 "type": "string"
139 },
140 "environment_vars": {
141 "type": "array",
142 "items": {
143 "type": "object",
144 "properties": {
145 "name": {
146 "type": "string"
147 },
148 "value": {
149 "type": "string"
150 }
151 }
152 }
153 },
154 "contract": {
155 "type": "object",
156 "properties": {
157 "id": {
158 "type": "string"
159 },
160 "name": {
161 "type": "string"
162 },
163 "created_at": {
164 "type": "string"
165 },
166 "products": {
167 "type": "array",
168 "items": {
169 "type": "string"
170 }
171 },
172 "tech_support_level": {
173 "type": "string"
174 }
175 }
176 },
177 "account": {
178 "type": "object",
179 "properties": {
180 "id": {
181 "type": "string"
182 },
183 "name": {
184 "type": "string"
185 },
186 "created_at": {
187 "type": "string"
188 },
189 "external_account_ids": {
190 "type": "array"
191 }
192 }
193 },
194 "config": {
195 "type": "object",
196 "properties": {
197 "contract_url": {
198 "type": "string"
199 },
200 "security_url": {
201 "type": "string"
202 },
203 "data_dir": {
204 "type": "string"
205 },
206 "log_level": {
207 "type": "string"
208 },
209 "log_file": {
210 "type": "string"
211 },
212 "timer_log_file": {
213 "type": "string"
214 },
215 "license_check_log_file": {
216 "type": "string"
217 },
218 "ua_config": {
219 "type": "object",
220 "properties": {
221 "apt_http_proxy": {
222 "type": ["null", "string"]
223 },
224 "apt_https_proxy": {
225 "type": ["null", "string"]
226 },
227 "http_proxy": {
228 "type": ["null", "string"]
229 },
230 "https_proxy": {
231 "type": ["null", "string"]
232 },
233 "update_messaging_timer": {
234 "type": "integer"
235 },
236 "update_status_timer": {
237 "type": "integer"
238 },
239 "metering_timer": {
240 "type": "integer"
241 }
242 }
243 }
244 }
245 }
246 }
247}
diff --git a/features/steps/steps.py b/features/steps/steps.py
index 2c0d5bf..e63f625 100644
--- a/features/steps/steps.py
+++ b/features/steps/steps.py
@@ -6,6 +6,7 @@ import shlex
6import subprocess6import subprocess
7import time7import time
88
9import jsonschema # type: ignore
9import yaml10import yaml
10from behave import given, then, when11from behave import given, then, when
11from hamcrest import (12from hamcrest import (
@@ -20,7 +21,12 @@ from features.environment import (
20 capture_container_as_image,21 capture_container_as_image,
21 create_instance_with_uat_installed,22 create_instance_with_uat_installed,
22)23)
23from features.util import SLOW_CMDS, emit_spinner_on_travis, nullcontext24from features.util import (
25 SLOW_CMDS,
26 SafeLoaderWithoutDatetime,
27 emit_spinner_on_travis,
28 nullcontext,
29)
24from uaclient.defaults import DEFAULT_CONFIG_FILE, DEFAULT_MACHINE_TOKEN_PATH30from uaclient.defaults import DEFAULT_CONFIG_FILE, DEFAULT_MACHINE_TOKEN_PATH
25from uaclient.util import DatetimeAwareJSONDecoder31from uaclient.util import DatetimeAwareJSONDecoder
2632
@@ -191,7 +197,7 @@ def when_i_run_command_on_machine(context, command, user_spec, instance_name):
191197
192@when("I verify `{file_name}` is empty on `{instance_name}` machine")198@when("I verify `{file_name}` is empty on `{instance_name}` machine")
193def when_i_verify_file_is_empty_on_machine(context, file_name, instance_name):199def when_i_verify_file_is_empty_on_machine(context, file_name, instance_name):
194 command = 'sh -c "cat {} | wc -l"'200 command = 'sh -c "cat {} | wc -l"'.format(file_name)
195 when_i_run_command(201 when_i_run_command(
196 context, command, user_spec="with sudo", instance_name=instance_name202 context, command, user_spec="with sudo", instance_name=instance_name
197 )203 )
@@ -230,8 +236,8 @@ def when_i_run_command_with_stdin(
230236
231237
232@when("I do a preflight check for `{contract_token}` {user_spec}")238@when("I do a preflight check for `{contract_token}` {user_spec}")
233def when_i_preflight(context, contract_token, user_spec):239def when_i_preflight(context, contract_token, user_spec, verify_return=True):
234 token = getattr(context.config, contract_token)240 token = getattr(context.config, contract_token, "invalid_token")
235 command = "ua status --simulate-with-token {}".format(token)241 command = "ua status --simulate-with-token {}".format(token)
236 if user_spec == "with the all flag":242 if user_spec == "with the all flag":
237 command += " --all"243 command += " --all"
@@ -239,10 +245,23 @@ def when_i_preflight(context, contract_token, user_spec):
239 output_format = user_spec.split()[2]245 output_format = user_spec.split()[2]
240 command += " --format {}".format(output_format)246 command += " --format {}".format(output_format)
241 when_i_run_command(247 when_i_run_command(
242 context=context, command=command, user_spec="as non-root"248 context=context,
249 command=command,
250 user_spec="as non-root",
251 verify_return=verify_return,
243 )252 )
244253
245254
255@when(
256 "I verify that a preflight check for `{contract_token}` {user_spec} exits {exit_codes}" # noqa
257)
258def when_i_attempt_preflight(context, contract_token, user_spec, exit_codes):
259 when_i_preflight(context, contract_token, user_spec, verify_return=False)
260
261 expected_codes = exit_codes.split(",")
262 assert str(context.process.returncode) in expected_codes
263
264
246@when("I run `{command}` {user_spec}")265@when("I run `{command}` {user_spec}")
247def when_i_run_command(266def when_i_run_command(
248 context,267 context,
@@ -280,6 +299,9 @@ def when_i_run_command(
280 logging.error("Error executing command: {}".format(command))299 logging.error("Error executing command: {}".format(command))
281 logging.error("stdout: {}".format(result.stdout))300 logging.error("stdout: {}".format(result.stdout))
282 logging.error("stderr: {}".format(result.stderr))301 logging.error("stderr: {}".format(result.stderr))
302 else:
303 logging.debug("stdout: {}".format(result.stdout))
304 logging.debug("stderr: {}".format(result.stderr))
283305
284 if verify_return:306 if verify_return:
285 assert_that(process.returncode, equal_to(0))307 assert_that(process.returncode, equal_to(0))
@@ -290,11 +312,21 @@ def when_i_run_command(
290@when("I fix `{issue}` by attaching to a subscription with `{token_type}`")312@when("I fix `{issue}` by attaching to a subscription with `{token_type}`")
291def when_i_fix_a_issue_by_attaching(context, issue, token_type):313def when_i_fix_a_issue_by_attaching(context, issue, token_type):
292 token = getattr(context.config, token_type)314 token = getattr(context.config, token_type)
315
316 if (
317 token_type == "contract_token_staging"
318 or token_type == "contract_token_staging_expired"
319 ):
320 change_contract_endpoint_to_staging(context, user_spec="with sudo")
321 else:
322 change_contract_endpoint_to_production(context, user_spec="with sudo")
323
293 when_i_run_command(324 when_i_run_command(
294 context=context,325 context=context,
295 command="ua fix {}".format(issue),326 command="ua fix {}".format(issue),
296 user_spec="with sudo",327 user_spec="with sudo",
297 stdin="a\n{}\n".format(token),328 stdin="a\n{}\n".format(token),
329 verify_return=False,
298 )330 )
299331
300332
@@ -340,23 +372,46 @@ def when_i_update_contract_field_to_new_value(
340 )372 )
341373
342374
375def change_contract_endpoint_to_staging(context, user_spec):
376 when_i_run_command(
377 context,
378 "sed -i 's/contracts.can/contracts.staging.can/' {}".format(
379 DEFAULT_CONFIG_FILE
380 ),
381 user_spec,
382 )
383
384
385def change_contract_endpoint_to_production(context, user_spec):
386 when_i_run_command(
387 context,
388 "sed -i 's/contracts.staging.can/contracts.can/' {}".format(
389 DEFAULT_CONFIG_FILE
390 ),
391 user_spec,
392 )
393
394
395@when("I attach `{token_type}` {user_spec} and options `{options}`")
396def when_i_attach_staging_token_with_options(
397 context, token_type, user_spec, options
398):
399 when_i_attach_staging_token(
400 context, token_type, user_spec, options=options
401 )
402
403
343@when("I attach `{token_type}` {user_spec}")404@when("I attach `{token_type}` {user_spec}")
344def when_i_attach_staging_token(405def when_i_attach_staging_token(
345 context, token_type, user_spec, verify_return=True406 context, token_type, user_spec, verify_return=True, options=""
346):407):
347 token = getattr(context.config, token_type)408 token = getattr(context.config, token_type)
348 if (409 if (
349 token_type == "contract_token_staging"410 token_type == "contract_token_staging"
350 or token_type == "contract_token_staging_expired"411 or token_type == "contract_token_staging_expired"
351 ):412 ):
352 when_i_run_command(413 change_contract_endpoint_to_staging(context, user_spec)
353 context,414 cmd = "ua attach {} {}".format(token, options).strip()
354 "sed -i 's/contracts.can/contracts.staging.can/' {}".format(
355 DEFAULT_CONFIG_FILE
356 ),
357 user_spec,
358 )
359 cmd = "ua attach {}".format(token)
360 when_i_run_command(context, cmd, user_spec, verify_return=False)415 when_i_run_command(context, cmd, user_spec, verify_return=False)
361416
362 if verify_return:417 if verify_return:
@@ -420,6 +475,25 @@ def when_i_wait(context, seconds):
420 time.sleep(int(seconds))475 time.sleep(int(seconds))
421476
422477
478@when("I replace `{original}` in `{filename}` with `{new}`")
479def when_i_replace_string_in_file(context, original, filename, new):
480 when_i_run_command(
481 context,
482 "sed -i 's/{original}/{new}/' {filename}".format(
483 original=original, new=new, filename=filename
484 ),
485 "with sudo",
486 )
487
488
489@when("I replace `{original}` in `{filename}` with token `{token_name}`")
490def when_i_replace_string_in_file_with_token(
491 context, original, filename, token_name
492):
493 token = getattr(context.config, token_name)
494 when_i_replace_string_in_file(context, original, filename, token)
495
496
423@then("I will see the following on stdout")497@then("I will see the following on stdout")
424def then_i_will_see_on_stdout(context):498def then_i_will_see_on_stdout(context):
425 assert_that(context.process.stdout.strip(), equal_to(context.text))499 assert_that(context.process.stdout.strip(), equal_to(context.text))
@@ -439,27 +513,6 @@ def then_conditional_stdout_does_not_match_regexp(context, value1, value2):
439 then_stream_does_not_match_regexp(context, "stdout")513 then_stream_does_not_match_regexp(context, "stdout")
440514
441515
442@then("stdout is formatted as `{output_format}` and has keys")
443def then_stdout_is_formatted_and_has_keys(context, output_format):
444 output = context.process.stdout.strip()
445 if output_format == "json":
446 data = json.loads(output)
447 elif output_format == "yaml":
448 data = yaml.safe_load(output)
449
450 keys = set(context.text.split())
451 output_keys = set(data.keys())
452
453 if keys != output_keys:
454 message = """
455 Missing keys in output: {}
456 Extra keys in output: {}
457 """.format(
458 keys - output_keys or "", output_keys - keys or ""
459 )
460 raise AssertionError(message)
461
462
463@then("{stream} does not match regexp")516@then("{stream} does not match regexp")
464def then_stream_does_not_match_regexp(context, stream):517def then_stream_does_not_match_regexp(context, stream):
465 content = getattr(context.process, stream).strip()518 content = getattr(context.process, stream).strip()
@@ -472,6 +525,12 @@ def then_stream_matches_regexp(context, stream):
472 assert_that(content, matches_regexp(context.text))525 assert_that(content, matches_regexp(context.text))
473526
474527
528@then("{stream} contains substring")
529def then_stream_contains_substring(context, stream):
530 content = getattr(context.process, stream).strip()
531 assert_that(content, contains_string(context.text))
532
533
475@then("I will see the following on stderr")534@then("I will see the following on stderr")
476def then_i_will_see_on_stderr(context):535def then_i_will_see_on_stderr(context):
477 assert_that(context.process.stderr.strip(), equal_to(context.text))536 assert_that(context.process.stderr.strip(), equal_to(context.text))
@@ -521,6 +580,16 @@ def then_i_verify_that_running_cmd_with_spec_exits_with_codes(
521580
522581
523@when(582@when(
583 "I verify that running attach `{spec}` with json response exits `{exit_codes}`" # noqa
584)
585def when_i_verify_attach_with_json_response(context, spec, exit_codes):
586 cmd = "ua attach {} --format json".format(context.config.contract_token)
587 then_i_verify_that_running_cmd_with_spec_exits_with_codes(
588 context=context, cmd_name=cmd, spec=spec, exit_codes=exit_codes
589 )
590
591
592@when(
524 "I verify that running `{cmd_name}` `{spec}` and stdin `{stdin}` exits `{exit_codes}`" # noqa593 "I verify that running `{cmd_name}` `{spec}` and stdin `{stdin}` exits `{exit_codes}`" # noqa
525)594)
526def then_i_verify_that_running_cmd_with_spec_and_stdin_exits_with_codes(595def then_i_verify_that_running_cmd_with_spec_and_stdin_exits_with_codes(
@@ -581,6 +650,16 @@ def verify_installed_package_matches_version_regexp(context, package, regex):
581 assert_that(context.process.stdout.strip(), matches_regexp(regex))650 assert_that(context.process.stdout.strip(), matches_regexp(regex))
582651
583652
653@then(
654 "I verify that packages `{packages}` installed versions match regexp `{regex}`" # noqa: E501
655)
656def verify_installed_packages_match_version_regexp(context, packages, regex):
657 for package in packages.split(" "):
658 verify_installed_package_matches_version_regexp(
659 context, package, regex
660 )
661
662
584@then("I verify that `{package}` is installed from apt source `{apt_source}`")663@then("I verify that `{package}` is installed from apt source `{apt_source}`")
585def verify_package_is_installed_from_apt_source(context, package, apt_source):664def verify_package_is_installed_from_apt_source(context, package, apt_source):
586 when_i_run_command(665 when_i_run_command(
@@ -609,6 +688,18 @@ def verify_package_is_installed_from_apt_source(context, package, apt_source):
609 )688 )
610689
611690
691@then(
692 "I verify that `{packages}` are installed from apt source `{apt_source}`"
693)
694def verify_packages_are_installed_from_apt_source(
695 context, packages, apt_source
696):
697 for package in packages.split(" "):
698 verify_package_is_installed_from_apt_source(
699 context, package, apt_source
700 )
701
702
612@then("I verify that the timer interval for `{job}` is `{interval}`")703@then("I verify that the timer interval for `{job}` is `{interval}`")
613def verify_timer_interval_for_job(context, job, interval):704def verify_timer_interval_for_job(context, job, interval):
614 when_i_run_command(705 when_i_run_command(
@@ -779,6 +870,18 @@ def i_restore_the_saved_key_value_on_contract(context, key):
779 )870 )
780871
781872
873@then("stdout is a {output_format} matching the `{schema}` schema")
874def stdout_matches_the_json_schema(context, output_format, schema):
875 if output_format == "json":
876 instance = json.loads(context.process.stdout.strip())
877 elif output_format == "yaml":
878 instance = yaml.load(
879 context.process.stdout.strip(), SafeLoaderWithoutDatetime
880 )
881 with open("features/schemas/{}.json".format(schema), "r") as schema_file:
882 jsonschema.validate(instance=instance, schema=json.load(schema_file))
883
884
782def get_command_prefix_for_user_spec(user_spec):885def get_command_prefix_for_user_spec(user_spec):
783 prefix = []886 prefix = []
784 if user_spec == "with sudo":887 if user_spec == "with sudo":
diff --git a/features/ubuntu_pro.feature b/features/ubuntu_pro.feature
index 95d54e1..04ef4fc 100644
--- a/features/ubuntu_pro.feature
+++ b/features/ubuntu_pro.feature
@@ -40,6 +40,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
40 """40 """
41 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools41 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
42 """42 """
43 When I run `ua enable <cis_or_usg>` with sudo
44 And I run `ua status` with sudo
45 Then stdout matches regexp:
46 """
47 <cis_or_usg> +yes +enabled +Security compliance and audit tools
48 """
49 When I run `ua disable <cis_or_usg>` with sudo
50 Then stdout matches regexp:
51 """
52 Updating package lists
53 """
54 When I run `ua status` with sudo
55 Then stdout matches regexp:
56 """
57 <cis_or_usg> +yes +disabled +Security compliance and audit tools
58 """
43 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine59 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
44 Then stdout matches regexp:60 Then stdout matches regexp:
45 """61 """
@@ -53,7 +69,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
53 | release | fips-s | cc-eal-s | cis-s | cis_or_usg |69 | release | fips-s | cc-eal-s | cis-s | cis_or_usg |
54 | xenial | disabled | disabled | disabled | cis |70 | xenial | disabled | disabled | disabled | cis |
55 | bionic | disabled | disabled | disabled | cis |71 | bionic | disabled | disabled | disabled | cis |
56 | focal | n/a | n/a | disabled | usg |72 | focal | disabled | n/a | disabled | usg |
5773
58 @series.lts74 @series.lts
59 @uses.config.machine_type.azure.pro75 @uses.config.machine_type.azure.pro
@@ -95,6 +111,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
95 """111 """
96 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools112 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
97 """113 """
114 When I run `ua enable <cis_or_usg>` with sudo
115 And I run `ua status` with sudo
116 Then stdout matches regexp:
117 """
118 <cis_or_usg> +yes +enabled +Security compliance and audit tools
119 """
120 When I run `ua disable <cis_or_usg>` with sudo
121 Then stdout matches regexp:
122 """
123 Updating package lists
124 """
125 When I run `ua status` with sudo
126 Then stdout matches regexp:
127 """
128 <cis_or_usg> +yes +disabled +Security compliance and audit tools
129 """
98 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine130 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
99 Then stdout matches regexp:131 Then stdout matches regexp:
100 """132 """
@@ -107,8 +139,8 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
107 Examples: ubuntu release139 Examples: ubuntu release
108 | release | fips-s | cc-eal-s | cis-s | livepatch-s | cis_or_usg |140 | release | fips-s | cc-eal-s | cis-s | livepatch-s | cis_or_usg |
109 | xenial | n/a | disabled | disabled | enabled | cis |141 | xenial | n/a | disabled | disabled | enabled | cis |
110 | bionic | disabled | disabled | disabled | n/a | cis |142 | bionic | disabled | disabled | disabled | enabled | cis |
111 | focal | n/a | n/a | disabled | enabled | usg |143 | focal | disabled | n/a | disabled | enabled | usg |
112144
113 @series.lts145 @series.lts
114 @uses.config.machine_type.gcp.pro146 @uses.config.machine_type.gcp.pro
@@ -150,6 +182,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
150 """182 """
151 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools183 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
152 """184 """
185 When I run `ua enable <cis_or_usg>` with sudo
186 And I run `ua status` with sudo
187 Then stdout matches regexp:
188 """
189 <cis_or_usg> +yes +enabled +Security compliance and audit tools
190 """
191 When I run `ua disable <cis_or_usg>` with sudo
192 Then stdout matches regexp:
193 """
194 Updating package lists
195 """
196 When I run `ua status` with sudo
197 Then stdout matches regexp:
198 """
199 <cis_or_usg> +yes +disabled +Security compliance and audit tools
200 """
153 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine201 When I run `cat /var/log/squid/access.log` `with sudo` on the `proxy` machine
154 Then stdout matches regexp:202 Then stdout matches regexp:
155 """203 """
@@ -163,7 +211,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
163 | release | fips-s | cc-eal-s | cis-s | livepatch-s | cis_or_usg |211 | release | fips-s | cc-eal-s | cis-s | livepatch-s | cis_or_usg |
164 | xenial | n/a | disabled | disabled | n/a | cis |212 | xenial | n/a | disabled | disabled | n/a | cis |
165 | bionic | disabled | disabled | disabled | n/a | cis |213 | bionic | disabled | disabled | disabled | n/a | cis |
166 | focal | n/a | n/a | disabled | enabled | usg |214 | focal | disabled | n/a | disabled | enabled | usg |
167215
168 @series.lts216 @series.lts
169 @uses.config.machine_type.aws.pro217 @uses.config.machine_type.aws.pro
@@ -178,7 +226,6 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
178 """226 """
179 And I run `ua auto-attach` with sudo227 And I run `ua auto-attach` with sudo
180 And I run `ua status --wait` as non-root228 And I run `ua status --wait` as non-root
181 And I run `ua status` as non-root
182 Then stdout matches regexp:229 Then stdout matches regexp:
183 """230 """
184 SERVICE ENTITLED STATUS DESCRIPTION231 SERVICE ENTITLED STATUS DESCRIPTION
@@ -214,6 +261,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
214 """261 """
215 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools262 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
216 """263 """
264 When I run `ua enable <cis_or_usg>` with sudo
265 And I run `ua status` with sudo
266 Then stdout matches regexp:
267 """
268 <cis_or_usg> +yes +enabled +Security compliance and audit tools
269 """
270 When I run `ua disable <cis_or_usg>` with sudo
271 Then stdout matches regexp:
272 """
273 Updating package lists
274 """
275 When I run `ua status` with sudo
276 Then stdout matches regexp:
277 """
278 <cis_or_usg> +yes +disabled +Security compliance and audit tools
279 """
217 When I run `systemctl start ua-auto-attach.service` with sudo280 When I run `systemctl start ua-auto-attach.service` with sudo
218 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`281 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`
219 Then stdout matches regexp:282 Then stdout matches regexp:
@@ -281,7 +344,7 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
281 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | cis_or_usg |344 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | cis_or_usg |
282 | xenial | disabled | disabled | disabled | libkrad0 | jq | cis |345 | xenial | disabled | disabled | disabled | libkrad0 | jq | cis |
283 | bionic | disabled | disabled | disabled | libkrad0 | bundler | cis |346 | bionic | disabled | disabled | disabled | libkrad0 | bundler | cis |
284 | focal | n/a | n/a | disabled | hello | ant | usg |347 | focal | disabled | n/a | disabled | hello | ant | usg |
285348
286 @series.lts349 @series.lts
287 @uses.config.machine_type.azure.pro350 @uses.config.machine_type.azure.pro
@@ -332,6 +395,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
332 """395 """
333 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools396 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
334 """397 """
398 When I run `ua enable <cis_or_usg>` with sudo
399 And I run `ua status` with sudo
400 Then stdout matches regexp:
401 """
402 <cis_or_usg> +yes +enabled +Security compliance and audit tools
403 """
404 When I run `ua disable <cis_or_usg>` with sudo
405 Then stdout matches regexp:
406 """
407 Updating package lists
408 """
409 When I run `ua status` with sudo
410 Then stdout matches regexp:
411 """
412 <cis_or_usg> +yes +disabled +Security compliance and audit tools
413 """
335 When I run `systemctl start ua-auto-attach.service` with sudo414 When I run `systemctl start ua-auto-attach.service` with sudo
336 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`415 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`
337 Then stdout matches regexp:416 Then stdout matches regexp:
@@ -398,8 +477,8 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
398 Examples: ubuntu release477 Examples: ubuntu release
399 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | livepatch | cis_or_usg |478 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | livepatch | cis_or_usg |
400 | xenial | n/a | disabled | disabled | libkrad0 | jq | enabled | cis |479 | xenial | n/a | disabled | disabled | libkrad0 | jq | enabled | cis |
401 | bionic | disabled | disabled | disabled | libkrad0 | bundler | n/a | cis |480 | bionic | disabled | disabled | disabled | libkrad0 | bundler | enabled | cis |
402 | focal | n/a | n/a | disabled | hello | ant | enabled | usg |481 | focal | disabled | n/a | disabled | hello | ant | enabled | usg |
403482
404 @series.lts483 @series.lts
405 @uses.config.machine_type.gcp.pro484 @uses.config.machine_type.gcp.pro
@@ -450,6 +529,22 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
450 """529 """
451 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools530 <cis_or_usg> +yes +<cis-s> +Security compliance and audit tools
452 """531 """
532 When I run `ua enable <cis_or_usg>` with sudo
533 And I run `ua status` with sudo
534 Then stdout matches regexp:
535 """
536 <cis_or_usg> +yes +enabled +Security compliance and audit tools
537 """
538 When I run `ua disable <cis_or_usg>` with sudo
539 Then stdout matches regexp:
540 """
541 Updating package lists
542 """
543 When I run `ua status` with sudo
544 Then stdout matches regexp:
545 """
546 <cis_or_usg> +yes +disabled +Security compliance and audit tools
547 """
453 When I run `systemctl start ua-auto-attach.service` with sudo548 When I run `systemctl start ua-auto-attach.service` with sudo
454 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`549 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`
455 Then stdout matches regexp:550 Then stdout matches regexp:
@@ -517,4 +612,4 @@ Feature: Command behaviour when auto-attached in an ubuntu PRO image
517 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | livepatch | cis_or_usg |612 | release | fips-s | cc-eal-s | cis-s | infra-pkg | apps-pkg | livepatch | cis_or_usg |
518 | xenial | n/a | disabled | disabled | libkrad0 | jq | n/a | cis |613 | xenial | n/a | disabled | disabled | libkrad0 | jq | n/a | cis |
519 | bionic | disabled | disabled | disabled | libkrad0 | bundler | n/a | cis |614 | bionic | disabled | disabled | disabled | libkrad0 | bundler | n/a | cis |
520 | focal | n/a | n/a | disabled | hello | ant | enabled | usg |615 | focal | disabled | n/a | disabled | hello | ant | enabled | usg |
diff --git a/features/ubuntu_pro_fips.feature b/features/ubuntu_pro_fips.feature
521new file mode 100644616new file mode 100644
index 0000000..7a18b03
--- /dev/null
+++ b/features/ubuntu_pro_fips.feature
@@ -0,0 +1,210 @@
1Feature: Command behaviour when auto-attached in an ubuntu PRO fips image
2
3 @series.lts
4 @uses.config.machine_type.azure.pro.fips
5 Scenario Outline: Check fips is enabled correctly on Ubuntu pro fips Azure machine
6 Given a `<release>` machine with ubuntu-advantage-tools installed
7 When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
8 """
9 contract_url: 'https://contracts.canonical.com'
10 data_dir: /var/lib/ubuntu-advantage
11 log_level: debug
12 log_file: /var/log/ubuntu-advantage.log
13 features:
14 allow_xenial_fips_on_cloud: true
15 """
16 And I run `ua auto-attach` with sudo
17 And I run `ua status --wait` as non-root
18 And I run `ua status` as non-root
19 Then stdout matches regexp:
20 """
21 esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
22 esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
23 fips +yes +enabled +NIST-certified core packages
24 fips-updates +yes +disabled +NIST-certified core packages with priority security updates
25 livepatch +yes +n/a +Canonical Livepatch service
26 """
27 And I verify that running `apt update` `with sudo` exits `0`
28 And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
29 And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
30 And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
31 And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
32 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
33 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
34 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
35 When I run `uname -r` as non-root
36 Then stdout matches regexp:
37 """
38 <fips-kernel-version>
39 """
40 When I run `apt-cache policy ubuntu-azure-fips` as non-root
41 Then stdout does not match regexp:
42 """
43 .*Installed: \(none\)
44 """
45 When I run `cat /proc/sys/crypto/fips_enabled` with sudo
46 Then I will see the following on stdout:
47 """
48 1
49 """
50 When I run `systemctl start ua-auto-attach.service` with sudo
51 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`
52 Then stdout matches regexp:
53 """
54 .*status=0\/SUCCESS.*
55 """
56 And stdout matches regexp:
57 """
58 Skipping attach: Instance '[0-9a-z\-]+' is already attached.
59 """
60 When I run `ua auto-attach` with sudo
61 Then stderr matches regexp:
62 """
63 Skipping attach: Instance '[0-9a-z\-]+' is already attached.
64 """
65 When I run `apt-cache policy` with sudo
66 Then apt-cache policy for the following url has permission `500`
67 """
68 https://esm.ubuntu.com/infra/ubuntu <release>-infra-updates/main amd64 Packages
69 """
70 And apt-cache policy for the following url has permission `500`
71 """
72 https://esm.ubuntu.com/infra/ubuntu <release>-infra-security/main amd64 Packages
73 """
74 And apt-cache policy for the following url has permission `500`
75 """
76 https://esm.ubuntu.com/apps/ubuntu <release>-apps-updates/main amd64 Packages
77 """
78 And apt-cache policy for the following url has permission `500`
79 """
80 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
81 """
82 And I verify that running `apt update` `with sudo` exits `0`
83 When I run `apt install -y <infra-pkg>/<release>-infra-security` with sudo, retrying exit [100]
84 And I run `apt-cache policy <infra-pkg>` as non-root
85 Then stdout matches regexp:
86 """
87 \s*500 https://esm.ubuntu.com/infra/ubuntu <release>-infra-security/main amd64 Packages
88 \s*500 https://esm.ubuntu.com/infra/ubuntu <release>-infra-updates/main amd64 Packages
89 """
90 And stdout matches regexp:
91 """
92 Installed: .*[~+]esm
93 """
94 When I run `apt install -y <apps-pkg>/<release>-apps-security` with sudo, retrying exit [100]
95 And I run `apt-cache policy <apps-pkg>` as non-root
96 Then stdout matches regexp:
97 """
98 Version table:
99 \s*\*\*\* .* 500
100 \s*500 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
101 """
102
103 Examples: ubuntu release
104 | release | infra-pkg | apps-pkg | fips-apt-source | fips-kernel-version |
105 | xenial | libkrad0 | jq | https://esm.ubuntu.com/fips/ubuntu xenial/main | fips |
106 | bionic | libkrad0 | bundler | https://esm.ubuntu.com/fips/ubuntu bionic/main | azure-fips |
107
108 @series.lts
109 @uses.config.machine_type.aws.pro.fips
110 Scenario Outline: Check fips is enabled correctly on Ubuntu pro fips AWS machine
111 Given a `<release>` machine with ubuntu-advantage-tools installed
112 When I create the file `/etc/ubuntu-advantage/uaclient.conf` with the following:
113 """
114 contract_url: 'https://contracts.canonical.com'
115 data_dir: /var/lib/ubuntu-advantage
116 log_level: debug
117 log_file: /var/log/ubuntu-advantage.log
118 """
119 And I run `ua auto-attach` with sudo
120 And I run `ua status --wait` as non-root
121 And I run `ua status` as non-root
122 Then stdout matches regexp:
123 """
124 esm-apps +yes +enabled +UA Apps: Extended Security Maintenance \(ESM\)
125 esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
126 fips +yes +enabled +NIST-certified core packages
127 fips-updates +yes +disabled +NIST-certified core packages with priority security updates
128 livepatch +yes +n/a +Canonical Livepatch service
129 """
130 And I verify that running `apt update` `with sudo` exits `0`
131 And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
132 And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
133 And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
134 And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
135 And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
136 And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
137 And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
138 When I run `uname -r` as non-root
139 Then stdout matches regexp:
140 """
141 <fips-kernel-version>
142 """
143 When I run `apt-cache policy ubuntu-aws-fips` as non-root
144 Then stdout does not match regexp:
145 """
146 .*Installed: \(none\)
147 """
148 When I run `cat /proc/sys/crypto/fips_enabled` with sudo
149 Then I will see the following on stdout:
150 """
151 1
152 """
153 When I run `systemctl start ua-auto-attach.service` with sudo
154 And I verify that running `systemctl status ua-auto-attach.service` `as non-root` exits `0,3`
155 Then stdout matches regexp:
156 """
157 .*status=0\/SUCCESS.*
158 """
159 And stdout matches regexp:
160 """
161 Skipping attach: Instance '[0-9a-z\-]+' is already attached.
162 """
163 When I run `ua auto-attach` with sudo
164 Then stderr matches regexp:
165 """
166 Skipping attach: Instance '[0-9a-z\-]+' is already attached.
167 """
168 When I run `apt-cache policy` with sudo
169 Then apt-cache policy for the following url has permission `500`
170 """
171 https://esm.ubuntu.com/infra/ubuntu <release>-infra-updates/main amd64 Packages
172 """
173 And apt-cache policy for the following url has permission `500`
174 """
175 https://esm.ubuntu.com/infra/ubuntu <release>-infra-security/main amd64 Packages
176 """
177 And apt-cache policy for the following url has permission `500`
178 """
179 https://esm.ubuntu.com/apps/ubuntu <release>-apps-updates/main amd64 Packages
180 """
181 And apt-cache policy for the following url has permission `500`
182 """
183 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
184 """
185 And I verify that running `apt update` `with sudo` exits `0`
186 When I run `apt install -y <infra-pkg>/<release>-infra-security` with sudo, retrying exit [100]
187 And I run `apt-cache policy <infra-pkg>` as non-root
188 Then stdout matches regexp:
189 """
190 \s*500 https://esm.ubuntu.com/infra/ubuntu <release>-infra-security/main amd64 Packages
191 \s*500 https://esm.ubuntu.com/infra/ubuntu <release>-infra-updates/main amd64 Packages
192 """
193 And stdout matches regexp:
194 """
195 Installed: .*[~+]esm
196 """
197 When I run `apt install -y <apps-pkg>/<release>-apps-security` with sudo, retrying exit [100]
198 And I run `apt-cache policy <apps-pkg>` as non-root
199 Then stdout matches regexp:
200 """
201 Version table:
202 \s*\*\*\* .* 500
203 \s*500 https://esm.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
204 """
205
206 Examples: ubuntu release
207 | release | infra-pkg | apps-pkg | fips-apt-source | fips-kernel-version |
208 | xenial | libkrad0 | jq | https://esm.ubuntu.com/fips/ubuntu xenial/main | fips |
209 | bionic | libkrad0 | bundler | https://esm.ubuntu.com/fips/ubuntu bionic/main | aws-fips |
210
diff --git a/features/ubuntu_upgrade.feature b/features/ubuntu_upgrade.feature
index fb506b9..4cf3407 100644
--- a/features/ubuntu_upgrade.feature
+++ b/features/ubuntu_upgrade.feature
@@ -3,7 +3,6 @@ Feature: Upgrade between releases when uaclient is attached
33
4 @slow4 @slow
5 @series.focal5 @series.focal
6 @series.hirsute
7 @series.impish6 @series.impish
8 @uses.config.machine_type.lxd.container7 @uses.config.machine_type.lxd.container
9 @upgrade8 @upgrade
@@ -11,6 +10,8 @@ Feature: Upgrade between releases when uaclient is attached
11 Given a `<release>` machine with ubuntu-advantage-tools installed10 Given a `<release>` machine with ubuntu-advantage-tools installed
12 When I attach `contract_token` with sudo11 When I attach `contract_token` with sudo
13 And I run `apt-get dist-upgrade --assume-yes` with sudo12 And I run `apt-get dist-upgrade --assume-yes` with sudo
13 # Some packages upgrade may require a reboot
14 And I reboot the `<release>` machine
14 And I create the file `/etc/update-manager/release-upgrades.d/ua-test.cfg` with the following15 And I create the file `/etc/update-manager/release-upgrades.d/ua-test.cfg` with the following
15 """16 """
16 [Sources]17 [Sources]
@@ -41,8 +42,7 @@ Feature: Upgrade between releases when uaclient is attached
4142
42 Examples: ubuntu release43 Examples: ubuntu release
43 | release | next_release | devel_release |44 | release | next_release | devel_release |
44 | focal | hirsute | |45 | focal | impish | |
45 | hirsute | impish | |
46 | impish | jammy | --devel-release |46 | impish | jammy | --devel-release |
4747
48 @slow48 @slow
diff --git a/features/ubuntu_upgrade_unattached.feature b/features/ubuntu_upgrade_unattached.feature
index 3944dba..0856c92 100644
--- a/features/ubuntu_upgrade_unattached.feature
+++ b/features/ubuntu_upgrade_unattached.feature
@@ -3,13 +3,14 @@ Feature: Upgrade between releases when uaclient is unattached
33
4 @slow4 @slow
5 @series.focal5 @series.focal
6 @series.hirsute
7 @series.impish6 @series.impish
8 @uses.config.machine_type.lxd.container7 @uses.config.machine_type.lxd.container
9 @upgrade8 @upgrade
10 Scenario Outline: Unattached upgrade across releases9 Scenario Outline: Unattached upgrade across releases
11 Given a `<release>` machine with ubuntu-advantage-tools installed10 Given a `<release>` machine with ubuntu-advantage-tools installed
12 When I run `apt-get dist-upgrade --assume-yes` with sudo11 When I run `apt-get dist-upgrade --assume-yes` with sudo
12 # Some packages upgrade may require a reboot
13 And I reboot the `<release>` machine
13 And I create the file `/etc/update-manager/release-upgrades.d/ua-test.cfg` with the following14 And I create the file `/etc/update-manager/release-upgrades.d/ua-test.cfg` with the following
14 """15 """
15 [Sources]16 [Sources]
@@ -40,8 +41,7 @@ Feature: Upgrade between releases when uaclient is unattached
4041
41 Examples: ubuntu release42 Examples: ubuntu release
42 | release | next_release | devel_release |43 | release | next_release | devel_release |
43 | focal | hirsute | |44 | focal | impish | |
44 | hirsute | impish | |
45 | impish | jammy | --devel-release |45 | impish | jammy | --devel-release |
4646
47 @slow47 @slow
diff --git a/features/unattached_commands.feature b/features/unattached_commands.feature
index bda7597..7c95ab9 100644
--- a/features/unattached_commands.feature
+++ b/features/unattached_commands.feature
@@ -27,7 +27,6 @@ Feature: Command behaviour when unattached
27 | bionic |27 | bionic |
28 | focal |28 | focal |
29 | xenial |29 | xenial |
30 | hirsute |
31 | impish |30 | impish |
32 | jammy |31 | jammy |
3332
@@ -131,8 +130,6 @@ Feature: Command behaviour when unattached
131 | focal | refresh |130 | focal | refresh |
132 | xenial | detach |131 | xenial | detach |
133 | xenial | refresh |132 | xenial | refresh |
134 | hirsute | detach |
135 | hirsute | refresh |
136 | impish | detach |133 | impish | detach |
137 | impish | refresh |134 | impish | refresh |
138 | jammy | detach |135 | jammy | detach |
@@ -169,10 +166,6 @@ Feature: Command behaviour when unattached
169 | xenial | disable | livepatch |166 | xenial | disable | livepatch |
170 | xenial | enable | unknown |167 | xenial | enable | unknown |
171 | xenial | disable | unknown |168 | xenial | disable | unknown |
172 | hirsute | enable | livepatch |
173 | hirsute | disable | livepatch |
174 | hirsute | enable | unknown |
175 | hirsute | disable | unknown |
176 | impish | enable | livepatch |169 | impish | enable | livepatch |
177 | impish | disable | livepatch |170 | impish | disable | livepatch |
178 | impish | enable | unknown |171 | impish | enable | unknown |
@@ -220,7 +213,6 @@ Feature: Command behaviour when unattached
220 | bionic | yes |213 | bionic | yes |
221 | focal | yes |214 | focal | yes |
222 | xenial | yes |215 | xenial | yes |
223 | hirsute | no |
224 | impish | no |216 | impish | no |
225 | jammy | no |217 | jammy | no |
226218
@@ -251,7 +243,6 @@ Feature: Command behaviour when unattached
251 | xenial |243 | xenial |
252 | bionic |244 | bionic |
253 | focal |245 | focal |
254 | hirsute |
255 | impish |246 | impish |
256 | jammy |247 | jammy |
257248
@@ -288,7 +279,7 @@ Feature: Command behaviour when unattached
288 USN-4539-1: AWL vulnerability279 USN-4539-1: AWL vulnerability
289 Found CVEs:280 Found CVEs:
290 https://ubuntu.com/security/CVE-2020-11728281 https://ubuntu.com/security/CVE-2020-11728
291 1 affected package is installed: awl282 1 affected source package is installed: awl
292 \(1/1\) awl:283 \(1/1\) awl:
293 A fix is available in Ubuntu standard updates.284 A fix is available in Ubuntu standard updates.
294 .*\{ apt update && apt install --only-upgrade -y libawl-php \}.*285 .*\{ apt update && apt install --only-upgrade -y libawl-php \}.*
@@ -299,7 +290,7 @@ Feature: Command behaviour when unattached
299 """290 """
300 CVE-2020-28196: Kerberos vulnerability291 CVE-2020-28196: Kerberos vulnerability
301 https://ubuntu.com/security/CVE-2020-28196292 https://ubuntu.com/security/CVE-2020-28196
302 1 affected package is installed: krb5293 1 affected source package is installed: krb5
303 \(1/1\) krb5:294 \(1/1\) krb5:
304 A fix is available in Ubuntu standard updates.295 A fix is available in Ubuntu standard updates.
305 The update is already installed.296 The update is already installed.
@@ -323,7 +314,7 @@ Feature: Command behaviour when unattached
323 USN-4539-1: AWL vulnerability314 USN-4539-1: AWL vulnerability
324 Found CVEs:315 Found CVEs:
325 https://ubuntu.com/security/CVE-2020-11728316 https://ubuntu.com/security/CVE-2020-11728
326 1 affected package is installed: awl317 1 affected source package is installed: awl
327 \(1/1\) awl:318 \(1/1\) awl:
328 Sorry, no fix is available.319 Sorry, no fix is available.
329 1 package is still affected: awl320 1 package is still affected: awl
@@ -334,7 +325,7 @@ Feature: Command behaviour when unattached
334 """325 """
335 CVE-2020-15180: MariaDB vulnerabilities326 CVE-2020-15180: MariaDB vulnerabilities
336 https://ubuntu.com/security/CVE-2020-15180327 https://ubuntu.com/security/CVE-2020-15180
337 No affected packages are installed.328 No affected source packages are installed.
338 .*✔.* CVE-2020-15180 does not affect your system.329 .*✔.* CVE-2020-15180 does not affect your system.
339 """330 """
340 When I run `ua fix CVE-2020-28196` as non-root331 When I run `ua fix CVE-2020-28196` as non-root
@@ -342,7 +333,7 @@ Feature: Command behaviour when unattached
342 """333 """
343 CVE-2020-28196: Kerberos vulnerability334 CVE-2020-28196: Kerberos vulnerability
344 https://ubuntu.com/security/CVE-2020-28196335 https://ubuntu.com/security/CVE-2020-28196
345 1 affected package is installed: krb5336 1 affected source package is installed: krb5
346 \(1/1\) krb5:337 \(1/1\) krb5:
347 A fix is available in Ubuntu standard updates.338 A fix is available in Ubuntu standard updates.
348 The update is already installed.339 The update is already installed.
@@ -354,7 +345,7 @@ Feature: Command behaviour when unattached
354 """345 """
355 CVE-2017-9233: Expat vulnerability346 CVE-2017-9233: Expat vulnerability
356 https://ubuntu.com/security/CVE-2017-9233347 https://ubuntu.com/security/CVE-2017-9233
357 3 affected packages are installed: expat, matanza, swish-e348 3 affected source packages are installed: expat, matanza, swish-e
358 \(1/3, 2/3\) matanza, swish-e:349 \(1/3, 2/3\) matanza, swish-e:
359 Sorry, no fix is available.350 Sorry, no fix is available.
360 \(3/3\) expat:351 \(3/3\) expat:
@@ -363,6 +354,28 @@ Feature: Command behaviour when unattached
363 2 packages are still affected: matanza, swish-e354 2 packages are still affected: matanza, swish-e
364 .*✘.* CVE-2017-9233 is not resolved.355 .*✘.* CVE-2017-9233 is not resolved.
365 """356 """
357 When I fix `USN-5079-2` by attaching to a subscription with `contract_token_staging_expired`
358 Then stdout matches regexp
359 """
360 USN-5079-2: curl vulnerabilities
361 Found CVEs:
362 https://ubuntu.com/security/CVE-2021-22946
363 https://ubuntu.com/security/CVE-2021-22947
364 1 affected source package is installed: curl
365 \(1/1\) curl:
366 A fix is available in UA Infra.
367 The update is not installed because this system is not attached to a
368 subscription.
369
370 Choose: \[S\]ubscribe at ubuntu.com \[A\]ttach existing token \[C\]ancel
371 > Enter your token \(from https://ubuntu.com/advantage\) to attach this system:
372 > .*\{ ua attach .*\}.*
373 Attach denied:
374 Contract ".*" expired on .*
375 Visit https://ubuntu.com/advantage to manage contract tokens.
376 1 package is still affected: curl
377 .*✘.* USN-5079-2 is not resolved.
378 """
366 When I fix `USN-5079-2` by attaching to a subscription with `contract_token`379 When I fix `USN-5079-2` by attaching to a subscription with `contract_token`
367 Then stdout matches regexp:380 Then stdout matches regexp:
368 """381 """
@@ -370,7 +383,7 @@ Feature: Command behaviour when unattached
370 Found CVEs:383 Found CVEs:
371 https://ubuntu.com/security/CVE-2021-22946384 https://ubuntu.com/security/CVE-2021-22946
372 https://ubuntu.com/security/CVE-2021-22947385 https://ubuntu.com/security/CVE-2021-22947
373 1 affected package is installed: curl386 1 affected source package is installed: curl
374 \(1/1\) curl:387 \(1/1\) curl:
375 A fix is available in UA Infra.388 A fix is available in UA Infra.
376 The update is not installed because this system is not attached to a389 The update is not installed because this system is not attached to a
@@ -395,7 +408,7 @@ Feature: Command behaviour when unattached
395 USN-5051-2: OpenSSL vulnerability408 USN-5051-2: OpenSSL vulnerability
396 Found CVEs:409 Found CVEs:
397 https://ubuntu.com/security/CVE-2021-3712410 https://ubuntu.com/security/CVE-2021-3712
398 1 affected package is installed: openssl411 1 affected source package is installed: openssl
399 \(1/1\) openssl:412 \(1/1\) openssl:
400 A fix is available in UA Infra.413 A fix is available in UA Infra.
401 .*\{ apt update && apt install --only-upgrade -y libssl1.0.0 openssl \}.*414 .*\{ apt update && apt install --only-upgrade -y libssl1.0.0 openssl \}.*
@@ -440,7 +453,7 @@ Feature: Command behaviour when unattached
440 USN-4539-1: AWL vulnerability453 USN-4539-1: AWL vulnerability
441 Found CVEs:454 Found CVEs:
442 https://ubuntu.com/security/CVE-2020-11728455 https://ubuntu.com/security/CVE-2020-11728
443 1 affected package is installed: awl456 1 affected source package is installed: awl
444 \(1/1\) awl:457 \(1/1\) awl:
445 Ubuntu security engineers are investigating this issue.458 Ubuntu security engineers are investigating this issue.
446 1 package is still affected: awl459 1 package is still affected: awl
@@ -451,7 +464,7 @@ Feature: Command behaviour when unattached
451 """464 """
452 CVE-2020-28196: Kerberos vulnerability465 CVE-2020-28196: Kerberos vulnerability
453 https://ubuntu.com/security/CVE-2020-28196466 https://ubuntu.com/security/CVE-2020-28196
454 1 affected package is installed: krb5467 1 affected source package is installed: krb5
455 \(1/1\) krb5:468 \(1/1\) krb5:
456 A fix is available in Ubuntu standard updates.469 A fix is available in Ubuntu standard updates.
457 The update is already installed.470 The update is already installed.
@@ -463,7 +476,7 @@ Feature: Command behaviour when unattached
463 """476 """
464 CVE-2021-27135: xterm vulnerability477 CVE-2021-27135: xterm vulnerability
465 https://ubuntu.com/security/CVE-2021-27135478 https://ubuntu.com/security/CVE-2021-27135
466 1 affected package is installed: xterm479 1 affected source package is installed: xterm
467 \(1/1\) xterm:480 \(1/1\) xterm:
468 A fix is available in Ubuntu standard updates.481 A fix is available in Ubuntu standard updates.
469 Package fixes cannot be installed.482 Package fixes cannot be installed.
@@ -476,7 +489,7 @@ Feature: Command behaviour when unattached
476 """489 """
477 CVE-2021-27135: xterm vulnerability490 CVE-2021-27135: xterm vulnerability
478 https://ubuntu.com/security/CVE-2021-27135491 https://ubuntu.com/security/CVE-2021-27135
479 1 affected package is installed: xterm492 1 affected source package is installed: xterm
480 \(1/1\) xterm:493 \(1/1\) xterm:
481 A fix is available in Ubuntu standard updates.494 A fix is available in Ubuntu standard updates.
482 .*\{ apt update && apt install --only-upgrade -y xterm \}.*495 .*\{ apt update && apt install --only-upgrade -y xterm \}.*
@@ -487,12 +500,26 @@ Feature: Command behaviour when unattached
487 """500 """
488 CVE-2021-27135: xterm vulnerability501 CVE-2021-27135: xterm vulnerability
489 https://ubuntu.com/security/CVE-2021-27135502 https://ubuntu.com/security/CVE-2021-27135
490 1 affected package is installed: xterm503 1 affected source package is installed: xterm
491 \(1/1\) xterm:504 \(1/1\) xterm:
492 A fix is available in Ubuntu standard updates.505 A fix is available in Ubuntu standard updates.
493 The update is already installed.506 The update is already installed.
494 .*✔.* CVE-2021-27135 is resolved.507 .*✔.* CVE-2021-27135 is resolved.
495 """508 """
509 When I run `apt-get install libbz2-1.0=1.0.6-8.1 -y --allow-downgrades` with sudo
510 And I run `apt-get install bzip2=1.0.6-8.1 -y` with sudo
511 And I run `ua fix USN-4038-3` with sudo
512 Then stdout matches regexp:
513 """
514 USN-4038-3: bzip2 regression
515 Found Launchpad bugs:
516 https://launchpad.net/bugs/1834494
517 1 affected source package is installed: bzip2
518 \(1/1\) bzip2:
519 A fix is available in Ubuntu standard updates.
520 .*\{ apt update && apt install --only-upgrade -y bzip2 libbz2-1.0 \}.*
521 .*✔.* USN-4038-3 is resolved.
522 """
496523
497524
498 @series.all525 @series.all
@@ -535,6 +562,31 @@ Feature: Command behaviour when unattached
535 | release |562 | release |
536 | bionic |563 | bionic |
537 | focal |564 | focal |
538 | hirsute |565 | impish |
566 | jammy |
567
568 @series.all
569 @uses.config.machine_type.lxd.container
570 Scenario Outline: Unattached enable fails in a ubuntu machine
571 Given a `<release>` machine with ubuntu-advantage-tools installed
572 When I verify that running `ua enable esm-infra` `with sudo` exits `1`
573 Then I will see the following on stderr:
574 """
575 To use 'esm-infra' you need an Ubuntu Advantage subscription
576 Personal and community subscriptions are available at no charge
577 See https://ubuntu.com/advantage
578 """
579 When I verify that running `ua enable esm-infra --format json --assume-yes` `with sudo` exits `1`
580 Then stdout is a json matching the `ua_operation` schema
581 And I will see the following on stdout:
582 """
583 {"_schema_version": "0.1", "errors": [{"message": "To use 'esm-infra' you need an Ubuntu Advantage subscription\nPersonal and community subscriptions are available at no charge\nSee https://ubuntu.com/advantage", "message_code": "enable-failure-unattached", "service": null, "type": "system"}], "failed_services": [], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
584 """
585
586 Examples: ubuntu release
587 | release |
588 | xenial |
589 | bionic |
590 | focal |
539 | impish |591 | impish |
540 | jammy |592 | jammy |
diff --git a/features/unattached_status.feature b/features/unattached_status.feature
index 7b91df3..08538a2 100644
--- a/features/unattached_status.feature
+++ b/features/unattached_status.feature
@@ -5,18 +5,31 @@ Feature: Unattached status
5 Scenario Outline: Unattached status in a ubuntu machine - formatted5 Scenario Outline: Unattached status in a ubuntu machine - formatted
6 Given a `<release>` machine with ubuntu-advantage-tools installed6 Given a `<release>` machine with ubuntu-advantage-tools installed
7 When I run `ua status --format json` as non-root7 When I run `ua status --format json` as non-root
8 Then stdout is formatted as `json` and has keys:8 Then stdout is a json matching the `ua_status` schema
9 When I run `ua status --format yaml` as non-root
10 Then stdout is a yaml matching the `ua_status` schema
11 When I run `sed -i 's/contracts.can/invalidurl.notcan/' /etc/ubuntu-advantage/uaclient.conf` with sudo
12 And I verify that running `ua status --format json` `as non-root` exits `1`
13 Then stdout is a json matching the `ua_status` schema
14 And I will see the following on stdout:
9 """15 """
10 _doc _schema_version account attached config config_path contract effective16 {"environment_vars": [], "errors": [{"message": "Failed to connect to authentication server\nCheck your Internet connection and try again.", "message_code": "connectivity-error", "service": null, "type": "system"}], "result": "failure", "services": [], "warnings": []}
11 environment_vars execution_details execution_status expires machine_id notices
12 services version simulated
13 """17 """
14 When I run `ua status --format yaml` as non-root18 And I verify that running `ua status --format yaml` `as non-root` exits `1`
15 Then stdout is formatted as `yaml` and has keys:19 Then stdout is a yaml matching the `ua_status` schema
20 And I will see the following on stdout:
16 """21 """
17 _doc _schema_version account attached config config_path contract effective22 environment_vars: []
18 environment_vars execution_details execution_status expires machine_id notices23 errors:
19 services version simulated24 - message: 'Failed to connect to authentication server
25
26 Check your Internet connection and try again.'
27 message_code: connectivity-error
28 service: null
29 type: system
30 result: failure
31 services: []
32 warnings: []
20 """33 """
2134
22 Examples: ubuntu release35 Examples: ubuntu release
@@ -24,7 +37,6 @@ Feature: Unattached status
24 | bionic |37 | bionic |
25 | focal |38 | focal |
26 | xenial |39 | xenial |
27 | hirsute |
28 | impish |40 | impish |
29 | jammy |41 | jammy |
3042
@@ -32,7 +44,6 @@ Feature: Unattached status
32 @uses.config.machine_type.lxd.container44 @uses.config.machine_type.lxd.container
33 Scenario Outline: Unattached status in a ubuntu machine45 Scenario Outline: Unattached status in a ubuntu machine
34 Given a `<release>` machine with ubuntu-advantage-tools installed46 Given a `<release>` machine with ubuntu-advantage-tools installed
35 When I run `sed -i 's/contracts.can/contracts.staging.can/' /etc/ubuntu-advantage/uaclient.conf` with sudo
36 When I run `ua status` as non-root47 When I run `ua status` as non-root
37 Then stdout matches regexp:48 Then stdout matches regexp:
38 """49 """
@@ -128,63 +139,104 @@ Feature: Unattached status
128 | xenial | yes | yes | cis | yes | yes | yes | yes | yes | |139 | xenial | yes | yes | cis | yes | yes | yes | yes | yes | |
129 | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |140 | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |
130 | focal | yes | no | | yes | yes | yes | no | yes | usg |141 | focal | yes | no | | yes | yes | yes | no | yes | usg |
131 | hirsute | no | no | cis | no | no | no | no | no | |
132 | impish | no | no | cis | no | no | no | no | no | |142 | impish | no | no | cis | no | no | no | no | no | |
133 | jammy | no | no | cis | no | no | no | no | no | |143 | jammy | no | no | cis | no | no | no | no | no | |
134144
135 @series.all145 @series.all
136 @uses.config.machine_type.lxd.container146 @uses.config.machine_type.lxd.container
137 @uses.config.contract_token147 @uses.config.contract_token
148 @uses.config.contract_token_staging_expired
138 Scenario Outline: Simulate status in a ubuntu machine149 Scenario Outline: Simulate status in a ubuntu machine
139 Given a `<release>` machine with ubuntu-advantage-tools installed150 Given a `<release>` machine with ubuntu-advantage-tools installed
140 When I do a preflight check for `contract_token` without the all flag151 When I do a preflight check for `contract_token` without the all flag
141 Then stdout matches regexp:152 Then stdout matches regexp:
142 """153 """
143 SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION154 SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
144 cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages155 cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
145 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?156 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
146 ?esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)157 ?esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
147 fips <fips> +yes +no +NIST-certified core packages158 fips <fips> +yes +no +NIST-certified core packages
148 fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates159 fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
149 livepatch <livepatch> +yes +yes +Canonical Livepatch service160 livepatch <livepatch> +yes +yes +Canonical Livepatch service
150 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?161 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
151 """162 """
152 When I do a preflight check for `contract_token` with the all flag163 When I do a preflight check for `contract_token` with the all flag
153 Then stdout matches regexp:164 Then stdout matches regexp:
154 """165 """
155 SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION166 SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
156 cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages167 cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
157 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?168 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
158 ?esm-apps <esm-apps> +yes +yes +UA Apps: Extended Security Maintenance \(ESM\)169 ?esm-apps <esm-apps> +yes +yes +UA Apps: Extended Security Maintenance \(ESM\)
159 esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)170 esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
160 fips <fips> +yes +no +NIST-certified core packages171 fips <fips> +yes +no +NIST-certified core packages
161 fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates172 fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
162 livepatch <livepatch> +yes +yes +Canonical Livepatch service173 livepatch <livepatch> +yes +yes +Canonical Livepatch service
163 ros <ros> +yes +no +Security Updates for the Robot Operating System174 ros <ros> +yes +no +Security Updates for the Robot Operating System
164 ros-updates <ros> +yes +no +All Updates for the Robot Operating System175 ros-updates <ros> +yes +no +All Updates for the Robot Operating System
165 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?176 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
166 """177 """
167 When I do a preflight check for `contract_token` formatted as json178 When I do a preflight check for `contract_token` formatted as json
168 Then stdout is formatted as `json` and has keys:179 Then stdout is a json matching the `ua_status` schema
169 """
170 _doc _schema_version account attached config config_path contract effective
171 environment_vars execution_details execution_status expires machine_id notices
172 services version simulated
173 """
174 When I do a preflight check for `contract_token` formatted as yaml180 When I do a preflight check for `contract_token` formatted as yaml
175 Then stdout is formatted as `yaml` and has keys:181 Then stdout is a yaml matching the `ua_status` schema
176 """182 When I verify that a preflight check for `invalid_token` formatted as json exits 1
177 _doc _schema_version account attached config config_path contract effective183 Then stdout is a json matching the `ua_status` schema
178 environment_vars execution_details execution_status expires machine_id notices184 And I will see the following on stdout:
179 services version simulated185 """
180 """186 {"environment_vars": [], "errors": [{"message": "Invalid token. See https://ubuntu.com/advantage", "message_code": "attach-invalid-token", "service": null, "type": "system"}], "result": "failure", "services": [], "warnings": []}
187 """
188 When I verify that a preflight check for `invalid_token` formatted as yaml exits 1
189 Then stdout is a yaml matching the `ua_status` schema
190 And I will see the following on stdout:
191 """
192 environment_vars: []
193 errors:
194 - message: Invalid token. See https://ubuntu.com/advantage
195 message_code: attach-invalid-token
196 service: null
197 type: system
198 result: failure
199 services: []
200 warnings: []
201 """
202 When I run `sed -i 's/contracts.can/contracts.staging.can/' /etc/ubuntu-advantage/uaclient.conf` with sudo
203 And I verify that a preflight check for `contract_token_staging_expired` formatted as json exits 1
204 Then stdout is a json matching the `ua_status` schema
205 And stdout matches regexp:
206 """
207 \"result\": \"failure\"
208 """
209 And stdout matches regexp:
210 """
211 \"message\": \"Contract .* expired on .*\"
212 """
213 When I verify that a preflight check for `contract_token_staging_expired` formatted as yaml exits 1
214 Then stdout is a yaml matching the `ua_status` schema
215 Then stdout matches regexp:
216 """
217 errors:
218 - message: Contract .* expired on .*
219 """
220 When I verify that a preflight check for `contract_token_staging_expired` without the all flag exits 1
221 Then stdout matches regexp:
222 """
223 This token is not valid.
224 Contract \".*\" expired on .*
181225
226 SERVICE AVAILABLE ENTITLED AUTO_ENABLED DESCRIPTION
227 cc-eal <cc-eal> +yes +no +Common Criteria EAL2 Provisioning Packages
228 ?<cis>( +<cis-available> +yes +no +Security compliance and audit tools)?
229 ?esm-infra <esm-infra> +yes +yes +UA Infra: Extended Security Maintenance \(ESM\)
230 fips <fips> +yes +no +NIST-certified core packages
231 fips-updates <fips> +yes +no +NIST-certified core packages with priority security updates
232 livepatch <livepatch> +yes +yes +Canonical Livepatch service
233 ?<usg>( +<cis-available> +yes +no +Security compliance and audit tools)?
234 """
182235
183 Examples: ubuntu release236 Examples: ubuntu release
184 | release | esm-apps | cc-eal | cis | cis-available | fips | esm-infra | ros | livepatch | usg |237 | release | esm-apps | cc-eal | cis | cis-available | fips | esm-infra | ros | livepatch | usg |
185 | xenial | yes | yes | cis | yes | yes | yes | yes | yes | |238 | xenial | yes | yes | cis | yes | yes | yes | yes | yes | |
186 | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |239 | bionic | yes | yes | cis | yes | yes | yes | yes | yes | |
187 | focal | yes | no | | yes | yes | yes | no | yes | usg |240 | focal | yes | no | | yes | yes | yes | no | yes | usg |
188 | hirsute | no | no | cis | no | no | no | no | no | |
189 | impish | no | no | cis | no | no | no | no | no | |241 | impish | no | no | cis | no | no | no | no | no | |
190 | jammy | no | no | cis | no | no | no | no | no | |242 | jammy | no | no | cis | no | no | no | no | no | |
diff --git a/features/util.py b/features/util.py
index 8bb7dfe..7e00746 100644
--- a/features/util.py
+++ b/features/util.py
@@ -325,3 +325,10 @@ def emit_spinner_on_travis(msg: str = " "):
325 finally:325 finally:
326 print()326 print()
327 dot_process.terminate()327 dot_process.terminate()
328
329
330class SafeLoaderWithoutDatetime(yaml.SafeLoader):
331 yaml_implicit_resolvers = {
332 k: [r for r in v if r[0] != "tag:yaml.org,2002:timestamp"]
333 for k, v in yaml.SafeLoader.yaml_implicit_resolvers.items()
334 }
diff --git a/integration-requirements.txt b/integration-requirements.txt
index 69471fa..effd7bd 100644
--- a/integration-requirements.txt
+++ b/integration-requirements.txt
@@ -1,5 +1,6 @@
1# Integration testing1# Integration testing
2behave2behave
3jsonschema
3PyHamcrest4PyHamcrest
4pycloudlib @ git+https://github.com/canonical/pycloudlib.git@756a2c2de044ca60eaa7cdc76653d23a1339dc0a5pycloudlib @ git+https://github.com/canonical/pycloudlib.git@756a2c2de044ca60eaa7cdc76653d23a1339dc0a
56
diff --git a/lib/reboot_cmds.py b/lib/reboot_cmds.py
index 569b46a..c819dba 100644
--- a/lib/reboot_cmds.py
+++ b/lib/reboot_cmds.py
@@ -18,11 +18,10 @@ import logging
18import os18import os
19import sys19import sys
2020
21from uaclient import config, contract, lock, status21from uaclient import config, contract, exceptions, lock, messages
22from uaclient.cli import setup_logging22from uaclient.cli import setup_logging
23from uaclient.entitlements.fips import FIPSEntitlement23from uaclient.entitlements.fips import FIPSEntitlement
24from uaclient.exceptions import LockHeldError, UserFacingError24from uaclient.util import subp
25from uaclient.util import ProcessExecutionError, UrlError, subp
2625
27# Retry sleep backoff algorithm if lock is held.26# Retry sleep backoff algorithm if lock is held.
28# Lock may be held by auto-attach on systems with ubuntu-advantage-pro.27# Lock may be held by auto-attach on systems with ubuntu-advantage-pro.
@@ -34,7 +33,7 @@ def run_command(cmd, cfg):
34 try:33 try:
35 out, _ = subp(cmd.split(), capture=True)34 out, _ = subp(cmd.split(), capture=True)
36 logging.debug("Successfully executed cmd: {}".format(cmd))35 logging.debug("Successfully executed cmd: {}".format(cmd))
37 except ProcessExecutionError as exec_error:36 except exceptions.ProcessExecutionError as exec_error:
38 msg = (37 msg = (
39 "Failed running cmd: {}\n"38 "Failed running cmd: {}\n"
40 "Return code: {}\n"39 "Return code: {}\n"
@@ -75,7 +74,7 @@ def fix_pro_pkg_holds(cfg):
75 )74 )
76 try:75 try:
77 entitlement.install_packages(cleanup_on_failure=False)76 entitlement.install_packages(cleanup_on_failure=False)
78 except UserFacingError as e:77 except exceptions.UserFacingError as e:
79 logging.error(e.msg)78 logging.error(e.msg)
80 logging.warning(79 logging.warning(
81 "Failed to install packages at boot: {}".format(80 "Failed to install packages at boot: {}".format(
@@ -83,22 +82,22 @@ def fix_pro_pkg_holds(cfg):
83 )82 )
84 )83 )
85 sys.exit(1)84 sys.exit(1)
86 cfg.remove_notice("", status.MESSAGE_FIPS_REBOOT_REQUIRED)85 cfg.remove_notice("", messages.FIPS_SYSTEM_REBOOT_REQUIRED.msg)
8786
8887
89def refresh_contract(cfg):88def refresh_contract(cfg):
90 try:89 try:
91 contract.request_updated_contract(cfg)90 contract.request_updated_contract(cfg)
92 except UrlError as exc:91 except exceptions.UrlError as exc:
93 logging.exception(exc)92 logging.exception(exc)
94 logging.warning(status.MESSAGE_REFRESH_CONTRACT_FAILURE)93 logging.warning(messages.REFRESH_CONTRACT_FAILURE)
95 sys.exit(1)94 sys.exit(1)
9695
9796
98def process_remaining_deltas(cfg):97def process_remaining_deltas(cfg):
99 cmd = "/usr/bin/python3 /usr/lib/ubuntu-advantage/upgrade_lts_contract.py"98 cmd = "/usr/bin/python3 /usr/lib/ubuntu-advantage/upgrade_lts_contract.py"
100 run_command(cmd=cmd, cfg=cfg)99 run_command(cmd=cmd, cfg=cfg)
101 cfg.remove_notice("", status.MESSAGE_LIVEPATCH_LTS_REBOOT_REQUIRED)100 cfg.remove_notice("", messages.LIVEPATCH_LTS_REBOOT_REQUIRED)
102101
103102
104def process_reboot_operations(cfg):103def process_reboot_operations(cfg):
@@ -122,13 +121,13 @@ def process_reboot_operations(cfg):
122 process_remaining_deltas(cfg)121 process_remaining_deltas(cfg)
123122
124 cfg.delete_cache_key("marker-reboot-cmds")123 cfg.delete_cache_key("marker-reboot-cmds")
125 cfg.remove_notice("", status.MESSAGE_REBOOT_SCRIPT_FAILED)124 cfg.remove_notice("", messages.REBOOT_SCRIPT_FAILED)
126 logging.debug("Successfully ran all commands on reboot.")125 logging.debug("Successfully ran all commands on reboot.")
127 except Exception as e:126 except Exception as e:
128 msg = "Failed running commands on reboot."127 msg = "Failed running commands on reboot."
129 msg += str(e)128 msg += str(e)
130 logging.error(msg)129 logging.error(msg)
131 cfg.add_notice("", status.MESSAGE_REBOOT_SCRIPT_FAILED)130 cfg.add_notice("", messages.REBOOT_SCRIPT_FAILED)
132131
133132
134def main(cfg):133def main(cfg):
@@ -145,7 +144,7 @@ def main(cfg):
145 max_retries=MAX_RETRIES_ON_LOCK_HELD,144 max_retries=MAX_RETRIES_ON_LOCK_HELD,
146 ):145 ):
147 process_reboot_operations(cfg=cfg)146 process_reboot_operations(cfg=cfg)
148 except LockHeldError as e:147 except exceptions.LockHeldError as e:
149 logging.warning("Lock not released. %s", str(e.msg))148 logging.warning("Lock not released. %s", str(e.msg))
150 sys.exit(1)149 sys.exit(1)
151150
diff --git a/setup.py b/setup.py
index 4c92683..bcce7c5 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ import glob
55
6import setuptools6import setuptools
77
8from uaclient import defaults, util, version8from uaclient import defaults, version
99
10NAME = "ubuntu-advantage-tools"10NAME = "ubuntu-advantage-tools"
1111
@@ -39,7 +39,7 @@ def _get_version():
3939
4040
41def _get_data_files():41def _get_data_files():
42 data_files = [42 return [
43 ("/etc/ubuntu-advantage", ["uaclient.conf", "help_data.yaml"]),43 ("/etc/ubuntu-advantage", ["uaclient.conf", "help_data.yaml"]),
44 ("/etc/update-motd.d", glob.glob("update-motd.d/*")),44 ("/etc/update-motd.d", glob.glob("update-motd.d/*")),
45 ("/usr/lib/ubuntu-advantage", glob.glob("lib/[!_]*")),45 ("/usr/lib/ubuntu-advantage", glob.glob("lib/[!_]*")),
@@ -49,16 +49,8 @@ def _get_data_files():
49 ["release-upgrades.d/ubuntu-advantage-upgrades.cfg"],49 ["release-upgrades.d/ubuntu-advantage-upgrades.cfg"],
50 ),50 ),
51 (defaults.CONFIG_DEFAULTS["data_dir"], []),51 (defaults.CONFIG_DEFAULTS["data_dir"], []),
52 ("/lib/systemd/system", glob.glob("systemd/*")),
52 ]53 ]
53 rel_major, _rel_minor = util.get_platform_info()["release"].split(".", 1)
54 if rel_major == "14":
55 data_files.append(
56 ("/etc/apt/apt.conf.d", ["apt.conf.d/51ubuntu-advantage-esm"])
57 )
58 data_files.append(("/etc/init", glob.glob("upstart/*")))
59 else:
60 data_files.append(("/lib/systemd/system", glob.glob("systemd/*")))
61 return data_files
6254
6355
64setuptools.setup(56setuptools.setup(
diff --git a/sru/release-27.7/test_world_readable_logs.sh b/sru/release-27.7/test_world_readable_logs.sh
65new file mode 10064457new file mode 100644
index 0000000..ed687a7
--- /dev/null
+++ b/sru/release-27.7/test_world_readable_logs.sh
@@ -0,0 +1,66 @@
1series=$1
2deb=$2
3
4set -eE
5
6GREEN="\e[32m"
7RED="\e[31m"
8BLUE="\e[36m"
9END_COLOR="\e[0m"
10
11function cleanup {
12 lxc delete test --force
13}
14
15function on_err {
16 echo -e "${RED}Test Failed${END_COLOR}"
17 cleanup
18 exit 1
19}
20
21trap on_err ERR
22
23function print_and_run_cmd {
24 echo -e "${BLUE}Running:${END_COLOR}" "$@"
25 echo -e "${BLUE}Output:${END_COLOR}"
26 lxc exec test -- sh -c "$@"
27 echo
28}
29
30function explanatory_message {
31 echo -e "${BLUE}$@${END_COLOR}"
32}
33
34explanatory_message "Starting $series container and updating ubuntu-advantage-tools"
35lxc launch ubuntu-daily:$series test >/dev/null 2>&1
36sleep 10
37
38explanatory_message "Check that log is not world readable"
39print_and_run_cmd "ua version"
40print_and_run_cmd "head /var/log/ubuntu-advantage.log"
41print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log -perm 0600 | grep -qz ."
42
43lxc exec test -- apt-get update >/dev/null
44explanatory_message "installing new version of ubuntu-advantage-tools from local copy"
45lxc file push $deb test/tmp/ua.deb > /dev/null
46print_and_run_cmd "dpkg -i /tmp/ua.deb"
47print_and_run_cmd "ua version"
48
49explanatory_message "Check that log files permissions are still the same"
50print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log -perm 0600 | grep -qz ."
51
52explanatory_message "Check that logrotate command will create world readable files"
53print_and_run_cmd "logrotate --force /etc/logrotate.d/ubuntu-advantage-tools"
54print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log -perm 0644 | grep -qz ."
55print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log.1 -perm 0600 | grep -qz ."
56
57explanatory_message "Check that running logrotate again will stil make world readable files"
58# Just to add new entry to the log
59print_and_run_cmd "ua version"
60print_and_run_cmd "logrotate --force /etc/logrotate.d/ubuntu-advantage-tools"
61print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log -perm 0644 | grep -qz ."
62print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log.1 -perm 0644 | grep -qz ."
63print_and_run_cmd "find /var/log/ -name ubuntu-advantage.log.2.gz -perm 0600 | grep -qz ."
64
65echo -e "${GREEN}Test Passed${END_COLOR}"
66cleanup
diff --git a/tools/create-lp-release-branches.sh b/tools/create-lp-release-branches.sh
index 604df74..454af96 100755
--- a/tools/create-lp-release-branches.sh
+++ b/tools/create-lp-release-branches.sh
@@ -32,7 +32,7 @@ else
32 set -e32 set -e
33fi33fi
3434
35for release in xenial bionic focal hirsute impish35for release in xenial bionic focal impish
36do36do
37 echo37 echo
38 echo $release38 echo $release
@@ -48,7 +48,6 @@ do
48 xenial) version=${UA_VERSION}~16.04.1;;48 xenial) version=${UA_VERSION}~16.04.1;;
49 bionic) version=${UA_VERSION}~18.04.1;;49 bionic) version=${UA_VERSION}~18.04.1;;
50 focal) version=${UA_VERSION}~20.04.1;;50 focal) version=${UA_VERSION}~20.04.1;;
51 hirsute) version=${UA_VERSION}~21.04.1;;
52 impish) version=${UA_VERSION}~21.10.1;;51 impish) version=${UA_VERSION}~21.10.1;;
53 esac52 esac
54 dch_cmd=(dch -v ${version} -D ${release} -b "Backport new upstream release: (LP: #${SRU_BUG}) to $release")53 dch_cmd=(dch -v ${version} -D ${release} -b "Backport new upstream release: (LP: #${SRU_BUG}) to $release")
diff --git a/tools/refresh-aws-pro-ids b/tools/refresh-aws-pro-ids
index 5256a6c..7a89f5a 100755
--- a/tools/refresh-aws-pro-ids
+++ b/tools/refresh-aws-pro-ids
@@ -3,6 +3,7 @@
3import glob3import glob
4import os4import os
5import re5import re
6
6import yaml7import yaml
78
8from uaclient import util9from uaclient import util
@@ -19,7 +20,7 @@ git push upstream your-branch
19Create a new pull request @ https://github.com/canonical/ubuntu-advantage-client/pulls20Create a new pull request @ https://github.com/canonical/ubuntu-advantage-client/pulls
20"""21"""
2122
22EOL_RELEASES = ("trusty", ) # Releases we no longer test23EOL_RELEASES = ("trusty",) # Releases we no longer test
2324
2425
25def main():26def main():
@@ -33,35 +34,45 @@ def main():
33 aws_ids = {}34 aws_ids = {}
34 for aws_listing in glob.glob("listing-aws-pro-*"):35 for aws_listing in glob.glob("listing-aws-pro-*"):
35 m = re.match(36 m = re.match(
36 r"^listing-aws-pro-(?P<release>\w+).yaml$", aws_listing37 r"^listing-aws-pro-(fips-)?(?P<release>\w+).yaml$", aws_listing
37 )38 )
38 if not m:39 if not m:
39 print("Skipping unexpected listing file name: ", aws_listing)40 print("Skipping unexpected listing file name: ", aws_listing)
40 continue41 continue
41 elif m.group("release") in EOL_RELEASES:42 elif m.group("release") in EOL_RELEASES:
42 print(43 print(
43 "Skipping release %s. No longer CI on EOL releases" %44 "Skipping release %s. No longer CI on EOL releases"
44 m.group("release")45 % m.group("release")
45 )46 )
46 continue47 continue
47 listing = yaml.safe_load(open(aws_listing, "r"))48 listing = yaml.safe_load(open(aws_listing, "r"))
48 for md in listing['metadata']:49 for md in listing["metadata"]:
49 if md["key"] == "series":50 if md["key"] == "series":
50 release = md["value"]51 release = md["value"]
52 if "fips" in listing["productID"]:
53 release = release + "-fips"
51 break54 break
52 for externalID in listing['externalIDs']:55 for externalID in listing["externalIDs"]:
53 if externalID['origin'] == 'AWS':56 if externalID["origin"] == "AWS":
54 # TODO(handle multiple IDs)57 # TODO(handle multiple IDs)
55 [marketplace_id] = externalID['IDs']58 [marketplace_id] = externalID["IDs"]
56 break59 break
57 marketplace_id = marketplace_id.replace(MARKETPLACE_PREFIX, "")60 marketplace_id = marketplace_id.replace(MARKETPLACE_PREFIX, "")
58 out, _err = util.subp(61 out, _err = util.subp(
59 ["aws", "ec2", "describe-images", "--owners", "aws-marketplace",62 [
60 "--filters", "Name=product-code,Values={}".format(marketplace_id),63 "aws",
61 "--query", "sort_by(Images, &CreationDate)[-1].ImageId"]64 "ec2",
65 "describe-images",
66 "--owners",
67 "aws-marketplace",
68 "--filters",
69 "Name=product-code,Values={}".format(marketplace_id),
70 "--query",
71 "sort_by(Images, &CreationDate)[-1].ImageId",
72 ]
62 )73 )
63 ami_id = out.strip()74 ami_id = out.strip()
64 aws_ids[release] = ami_id.replace("\"", "")75 aws_ids[release] = ami_id.replace('"', "")
6576
66 os.chdir("../..")77 os.chdir("../..")
67 with open("features/aws-ids.yaml", "w") as stream:78 with open("features/aws-ids.yaml", "w") as stream:
diff --git a/tools/run-integration-tests.py b/tools/run-integration-tests.py
index 10b4a55..da55b1c 100644
--- a/tools/run-integration-tests.py
+++ b/tools/run-integration-tests.py
@@ -12,7 +12,6 @@ SERIES_TO_VERSION = {
12 "xenial": "16.04",12 "xenial": "16.04",
13 "bionic": "18.04",13 "bionic": "18.04",
14 "focal": "20.04",14 "focal": "20.04",
15 "hirsute": "21.04",
16 "impish": "21.10",15 "impish": "21.10",
17 "jammy": "22.04",16 "jammy": "22.04",
18}17}
@@ -28,11 +27,11 @@ PLATFORM_SERIES_TESTS = {
28 "azurepro": ["xenial", "bionic", "focal"],27 "azurepro": ["xenial", "bionic", "focal"],
29 "awsgeneric": ["xenial", "bionic", "focal"],28 "awsgeneric": ["xenial", "bionic", "focal"],
30 "awspro": ["xenial", "bionic", "focal"],29 "awspro": ["xenial", "bionic", "focal"],
31 "gcpgeneric": ["xenial", "bionic", "focal", "hirsute"],30 "gcpgeneric": ["xenial", "bionic", "focal", "impish", "jammy"],
32 "gcppro": ["xenial", "bionic", "focal"],31 "gcppro": ["xenial", "bionic", "focal"],
33 "vm": ["xenial", "bionic", "focal"],32 "vm": ["xenial", "bionic", "focal"],
34 "lxd": ["xenial", "bionic", "focal", "hirsute", "impish", "jammy"],33 "lxd": ["xenial", "bionic", "focal", "impish", "jammy"],
35 "upgrade": ["xenial", "bionic", "focal", "hirsute", "impish"],34 "upgrade": ["xenial", "bionic", "focal", "impish"],
36}35}
3736
3837
diff --git a/tox.ini b/tox.ini
index 9c98f03..45962e0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,20 +1,21 @@
1[tox]1[tox]
2envlist = py3, flake8, py3-{xenial,bionic}, flake8-{trusty,xenial,bionic}, mypy, black, isort2envlist = py3, flake8, py3-{xenial,bionic}, flake8-{xenial,bionic}, mypy, black, isort
3
4[testenv:flake8-trusty]
5pip_version = 9.0.2
63
7[testenv:flake8-bionic]4[testenv:flake8-bionic]
8pip_version = 9.0.25pip_version = 9.0.2
6setuptools_version = 59.8.0
97
10[testenv:flake8-xenial]8[testenv:flake8-xenial]
11pip_version = 9.0.29pip_version = 9.0.2
10setuptools_version = 59.8.0
1211
13[testenv:py3-xenial]12[testenv:py3-xenial]
14pip_version = 9.0.213pip_version = 9.0.2
14setuptools_version = 59.8.0
1515
16[testenv:py3-bionic]16[testenv:py3-bionic]
17pip_version = 9.0.217pip_version = 9.0.2
18setuptools_version = 59.8.0
1819
19[testenv]20[testenv]
20deps =21deps =
@@ -35,8 +36,10 @@ passenv =
35setenv =36setenv =
36 awsgeneric: UACLIENT_BEHAVE_MACHINE_TYPE = aws.generic37 awsgeneric: UACLIENT_BEHAVE_MACHINE_TYPE = aws.generic
37 awspro: UACLIENT_BEHAVE_MACHINE_TYPE = aws.pro38 awspro: UACLIENT_BEHAVE_MACHINE_TYPE = aws.pro
39 awspro-fips: UACLIENT_BEHAVE_MACHINE_TYPE = aws.pro.fips
38 azuregeneric: UACLIENT_BEHAVE_MACHINE_TYPE = azure.generic40 azuregeneric: UACLIENT_BEHAVE_MACHINE_TYPE = azure.generic
39 azurepro: UACLIENT_BEHAVE_MACHINE_TYPE = azure.pro41 azurepro: UACLIENT_BEHAVE_MACHINE_TYPE = azure.pro
42 azurepro-fips: UACLIENT_BEHAVE_MACHINE_TYPE = azure.pro.fips
40 gcpgeneric: UACLIENT_BEHAVE_MACHINE_TYPE = gcp.generic43 gcpgeneric: UACLIENT_BEHAVE_MACHINE_TYPE = gcp.generic
41 gcppro: UACLIENT_BEHAVE_MACHINE_TYPE = gcp.pro44 gcppro: UACLIENT_BEHAVE_MACHINE_TYPE = gcp.pro
42 vm: UACLIENT_BEHAVE_MACHINE_TYPE = lxd.vm45 vm: UACLIENT_BEHAVE_MACHINE_TYPE = lxd.vm
@@ -52,16 +55,14 @@ commands =
52 behave-lxd-16.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"55 behave-lxd-16.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
53 behave-lxd-18.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"56 behave-lxd-18.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
54 behave-lxd-20.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.focal,series.lts,series.all" --tags="~upgrade"57 behave-lxd-20.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
55 behave-lxd-21.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.hirsute,series.all" --tags="~upgrade"
56 behave-lxd-21.10: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.impish,series.all" --tags="~upgrade"58 behave-lxd-21.10: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.impish,series.all" --tags="~upgrade"
57 behave-lxd-22.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.jammy,series.all" --tags="~upgrade"59 behave-lxd-22.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.container" --tags="series.jammy,series.lts,series.all" --tags="~upgrade"
58 behave-vm-16.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.xenial,series.all,series.lts" --tags="~upgrade"60 behave-vm-16.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.xenial,series.all,series.lts" --tags="~upgrade"
59 behave-vm-18.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.bionic,series.all,series.lts" --tags="~upgrade"61 behave-vm-18.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.bionic,series.all,series.lts" --tags="~upgrade"
60 behave-vm-20.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.focal,series.all,series.lts" --tags="~upgrade"62 behave-vm-20.04: behave -v {posargs} --tags="uses.config.machine_type.lxd.vm" --tags="series.focal,series.all,series.lts" --tags="~upgrade"
61 behave-upgrade-16.04: behave -v {posargs} --tags="upgrade" --tags="series.xenial,series.all"63 behave-upgrade-16.04: behave -v {posargs} --tags="upgrade" --tags="series.xenial,series.all"
62 behave-upgrade-18.04: behave -v {posargs} --tags="upgrade" --tags="series.bionic,series.all"64 behave-upgrade-18.04: behave -v {posargs} --tags="upgrade" --tags="series.bionic,series.all"
63 behave-upgrade-20.04: behave -v {posargs} --tags="upgrade" --tags="series.focal,series.all"65 behave-upgrade-20.04: behave -v {posargs} --tags="upgrade" --tags="series.focal,series.all"
64 behave-upgrade-21.04: behave -v {posargs} --tags="upgrade" --tags="series.hirsute,series.all"
65 behave-upgrade-21.10: behave -v {posargs} --tags="upgrade" --tags="series.impish,series.all"66 behave-upgrade-21.10: behave -v {posargs} --tags="upgrade" --tags="series.impish,series.all"
66 behave-awsgeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"67 behave-awsgeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
67 behave-awsgeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"68 behave-awsgeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
@@ -69,16 +70,21 @@ commands =
69 behave-awspro-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.xenial,series.lts,series.all"70 behave-awspro-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.xenial,series.lts,series.all"
70 behave-awspro-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.bionic,series.lts,series.all"71 behave-awspro-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.bionic,series.lts,series.all"
71 behave-awspro-20.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.focal,series.lts,series.all"72 behave-awspro-20.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro" --tags="series.focal,series.lts,series.all"
73 behave-awspro-fips-16.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro.fips" --tags="series.xenial,series.lts,series.all"
74 behave-awspro-fips-18.04: behave -v {posargs} --tags="uses.config.machine_type.aws.pro.fips" --tags="series.bionic,series.lts,series.all"
72 behave-azuregeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"75 behave-azuregeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
73 behave-azuregeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"76 behave-azuregeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
74 behave-azuregeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"77 behave-azuregeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
75 behave-azurepro-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.xenial,series.lts,series.all"78 behave-azurepro-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.xenial,series.lts,series.all"
76 behave-azurepro-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.bionic,series.lts,series.all"79 behave-azurepro-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.bionic,series.lts,series.all"
77 behave-azurepro-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.focal,series.lts,series.all"80 behave-azurepro-20.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro" --tags="series.focal,series.lts,series.all"
81 behave-azurepro-fips-16.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro.fips" --tags="series.xenial,series.lts,series.all"
82 behave-azurepro-fips-18.04: behave -v {posargs} --tags="uses.config.machine_type.azure.pro.fips" --tags="series.bionic,series.lts,series.all"
78 behave-gcpgeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"83 behave-gcpgeneric-16.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
79 behave-gcpgeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"84 behave-gcpgeneric-18.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
80 behave-gcpgeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"85 behave-gcpgeneric-20.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
81 behave-gcpgeneric-21.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.hirsute,series.all" --tags="~upgrade"86 behave-gcpgeneric-21.10: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.impish,series.all" --tags="~upgrade"
87 behave-gcpgeneric-22.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.generic" --tags="series.jammy,series.lts,series.all" --tags="~upgrade"
82 behave-gcppro-16.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"88 behave-gcppro-16.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.xenial,series.lts,series.all" --tags="~upgrade"
83 behave-gcppro-18.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"89 behave-gcppro-18.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.bionic,series.lts,series.all" --tags="~upgrade"
84 behave-gcppro-20.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.focal,series.lts,series.all" --tags="~upgrade"90 behave-gcppro-20.04: behave -v {posargs} --tags="uses.config.machine_type.gcp.pro" --tags="series.focal,series.lts,series.all" --tags="~upgrade"
diff --git a/uaclient/actions.py b/uaclient/actions.py
index ec8706f..792c5a7 100644
--- a/uaclient/actions.py
+++ b/uaclient/actions.py
@@ -1,9 +1,20 @@
1import logging1import logging
2import sys
3from typing import Optional # noqa: F401
24
3from uaclient import clouds, config, contract, exceptions, status, util5from uaclient import (
6 clouds,
7 config,
8 contract,
9 entitlements,
10 event_logger,
11 exceptions,
12 messages,
13)
4from uaclient.clouds import identity14from uaclient.clouds import identity
515
6LOG = logging.getLogger("ua.actions")16LOG = logging.getLogger("ua.actions")
17event = event_logger.get_event_logger()
718
819
9def attach_with_token(20def attach_with_token(
@@ -22,18 +33,20 @@ def attach_with_token(
22 contract.request_updated_contract(33 contract.request_updated_contract(
23 cfg, token, allow_enable=allow_enable34 cfg, token, allow_enable=allow_enable
24 )35 )
25 except util.UrlError as exc:36 except exceptions.UrlError as exc:
26 with util.disable_log_to_console():
27 LOG.exception(exc)
28 cfg.status() # Persist updated status in the event of partial attach37 cfg.status() # Persist updated status in the event of partial attach
29 update_apt_and_motd_messages(cfg)38 update_apt_and_motd_messages(cfg)
30 raise exc39 raise exc
31 except exceptions.UserFacingError as exc:40 except exceptions.UserFacingError as exc:
32 LOG.warning(exc.msg)41 event.info(exc.msg, file_type=sys.stderr)
33 cfg.status() # Persist updated status in the event of partial attach42 cfg.status() # Persist updated status in the event of partial attach
34 update_apt_and_motd_messages(cfg)43 update_apt_and_motd_messages(cfg)
35 raise exc44 raise exc
3645
46 current_iid = identity.get_instance_id()
47 if current_iid:
48 cfg.write_cache("instance-id", current_iid)
49
37 update_apt_and_motd_messages(cfg)50 update_apt_and_motd_messages(cfg)
3851
3952
@@ -53,16 +66,53 @@ def auto_attach(
53 tokenResponse = contract_client.request_auto_attach_contract_token(66 tokenResponse = contract_client.request_auto_attach_contract_token(
54 instance=cloud67 instance=cloud
55 )68 )
56 except contract.ContractAPIError as e:69 except exceptions.ContractAPIError as e:
57 if e.code and 400 <= e.code < 500:70 if e.code and 400 <= e.code < 500:
58 raise exceptions.NonAutoAttachImageError(71 raise exceptions.NonAutoAttachImageError(
59 status.MESSAGE_UNSUPPORTED_AUTO_ATTACH72 messages.UNSUPPORTED_AUTO_ATTACH
60 )73 )
61 raise e74 raise e
62 current_iid = identity.get_instance_id()
63 if current_iid:
64 cfg.write_cache("instance-id", current_iid)
6575
66 token = tokenResponse["contractToken"]76 token = tokenResponse["contractToken"]
6777
68 attach_with_token(cfg, token=token, allow_enable=True)78 attach_with_token(cfg, token=token, allow_enable=True)
79
80
81def enable_entitlement_by_name(
82 cfg: config.UAConfig,
83 name: str,
84 *,
85 assume_yes: bool = False,
86 allow_beta: bool = False
87):
88 """
89 Constructs an entitlement based on the name provided. Passes kwargs onto
90 the entitlement constructor.
91 :raise EntitlementNotFoundError: If no entitlement with the given name is
92 found, then raises this error.
93 """
94 ent_cls = entitlements.entitlement_factory(name)
95 entitlement = ent_cls(
96 cfg, assume_yes=assume_yes, allow_beta=allow_beta, called_name=name
97 )
98 return entitlement.enable()
99
100
101def status(
102 cfg: config.UAConfig,
103 *,
104 simulate_with_token: Optional[str] = None,
105 show_beta: bool = False
106):
107 """
108 Construct the current UA status dictionary.
109 """
110 if simulate_with_token:
111 status, ret = cfg.simulate_status(
112 token=simulate_with_token, show_beta=show_beta
113 )
114 else:
115 status = cfg.status(show_beta=show_beta)
116 ret = 0
117
118 return status, ret
diff --git a/uaclient/apt.py b/uaclient/apt.py
index f6e86a7..e84d3cf 100644
--- a/uaclient/apt.py
+++ b/uaclient/apt.py
@@ -6,7 +6,7 @@ import subprocess
6import tempfile6import tempfile
7from typing import Dict, List, Optional7from typing import Dict, List, Optional
88
9from uaclient import exceptions, gpg, status, util9from uaclient import event_logger, exceptions, gpg, messages, util
1010
11APT_HELPER_TIMEOUT = 60.0 # 60 second timeout used for apt-helper call11APT_HELPER_TIMEOUT = 60.0 # 60 second timeout used for apt-helper call
12APT_AUTH_COMMENT = " # ubuntu-advantage-tools"12APT_AUTH_COMMENT = " # ubuntu-advantage-tools"
@@ -27,6 +27,8 @@ APT_PROXY_CONF_FILE = "/etc/apt/apt.conf.d/90ubuntu-advantage-aptproxy"
27# Hope for an optimal first try.27# Hope for an optimal first try.
28APT_RETRIES = [1.0, 5.0, 10.0]28APT_RETRIES = [1.0, 5.0, 10.0]
2929
30event = event_logger.get_event_logger()
31
3032
31def assert_valid_apt_credentials(repo_url, username, password):33def assert_valid_apt_credentials(repo_url, username, password):
32 """Validate apt credentials for a PPA.34 """Validate apt credentials for a PPA.
@@ -55,7 +57,7 @@ def assert_valid_apt_credentials(repo_url, username, password):
55 timeout=APT_HELPER_TIMEOUT,57 timeout=APT_HELPER_TIMEOUT,
56 retry_sleeps=APT_RETRIES,58 retry_sleeps=APT_RETRIES,
57 )59 )
58 except util.ProcessExecutionError as e:60 except exceptions.ProcessExecutionError as e:
59 if e.exit_code == 100:61 if e.exit_code == 100:
60 stderr = str(e.stderr).lower()62 stderr = str(e.stderr).lower()
61 if re.search(r"401\s+unauthorized|httperror401", stderr):63 if re.search(r"401\s+unauthorized|httperror401", stderr):
@@ -80,7 +82,9 @@ def assert_valid_apt_credentials(repo_url, username, password):
80 )82 )
8183
8284
83def _parse_apt_update_for_invalid_apt_config(apt_error: str) -> str:85def _parse_apt_update_for_invalid_apt_config(
86 apt_error: str
87) -> Optional[messages.NamedMessage]:
84 """Parse apt update errors for invalid apt config in user machine.88 """Parse apt update errors for invalid apt config in user machine.
8589
86 This functions parses apt update errors regarding the presence of90 This functions parses apt update errors regarding the presence of
@@ -96,9 +100,9 @@ def _parse_apt_update_for_invalid_apt_config(apt_error: str) -> str:
96 message.100 message.
97101
98 :param apt_error: The apt error string102 :param apt_error: The apt error string
99 :return: a string containing the parsed error message.103 :return: a NamedMessage containing the error message
100 """104 """
101 error_msg = ""105 error_msg = None
102 failed_repos = set()106 failed_repos = set()
103107
104 for line in apt_error.strip().split("\n"):108 for line in apt_error.strip().split("\n"):
@@ -115,17 +119,18 @@ def _parse_apt_update_for_invalid_apt_config(apt_error: str) -> str:
115 failed_repos.add(repo_url_match)119 failed_repos.add(repo_url_match)
116120
117 if failed_repos:121 if failed_repos:
118 error_msg += "\n"122 error_msg = messages.APT_UPDATE_INVALID_URL_CONFIG.format(
119 error_msg += status.MESSAGE_APT_UPDATE_INVALID_URL_CONFIG.format(123 plural="s" if len(failed_repos) > 1 else "",
120 "s" if len(failed_repos) > 1 else "",124 failed_repos="\n".join(sorted(failed_repos)),
121 "\n".join(sorted(failed_repos)),
122 )125 )
123126
124 return error_msg127 return error_msg
125128
126129
127def run_apt_command(130def run_apt_command(
128 cmd: List[str], error_msg: str, env: Optional[Dict[str, str]] = {}131 cmd: List[str],
132 error_msg: Optional[str] = None,
133 env: Optional[Dict[str, str]] = {},
129) -> str:134) -> str:
130 """Run an apt command, retrying upon failure APT_RETRIES times.135 """Run an apt command, retrying upon failure APT_RETRIES times.
131136
@@ -141,18 +146,66 @@ def run_apt_command(
141 out, _err = util.subp(146 out, _err = util.subp(
142 cmd, capture=True, retry_sleeps=APT_RETRIES, env=env147 cmd, capture=True, retry_sleeps=APT_RETRIES, env=env
143 )148 )
144 except util.ProcessExecutionError as e:149 except exceptions.ProcessExecutionError as e:
145 if "Could not get lock /var/lib/dpkg/lock" in str(e.stderr):150 if "Could not get lock /var/lib/dpkg/lock" in str(e.stderr):
146 error_msg += " Another process is running APT."151 raise exceptions.APTProcessConflictError()
147 else:152 else:
148 """153 """
149 Treat errors where one of the APT repositories154 Treat errors where one of the APT repositories
150 is invalid or unreachable. In that situation, we alert155 is invalid or unreachable. In that situation, we alert
151 which repository is causing the error156 which repository is causing the error
152 """157 """
153 error_msg += _parse_apt_update_for_invalid_apt_config(e.stderr)158 repo_error_msg = _parse_apt_update_for_invalid_apt_config(e.stderr)
159 if repo_error_msg:
160 raise exceptions.APTInvalidRepoError(
161 error_msg=repo_error_msg.msg
162 )
163
164 msg = error_msg if error_msg else str(e)
165 raise exceptions.UserFacingError(msg)
166 return out
167
168
169def run_apt_update_command(env: Optional[Dict[str, str]] = {}) -> str:
170 try:
171 out = run_apt_command(cmd=["apt-get", "update"], env=env)
172 except exceptions.APTProcessConflictError:
173 raise exceptions.APTUpdateProcessConflictError()
174 except exceptions.APTInvalidRepoError as e:
175 raise exceptions.APTUpdateInvalidRepoError(repo_msg=e.msg)
176 except exceptions.UserFacingError as e:
177 raise exceptions.UserFacingError(
178 msg=messages.APT_UPDATE_FAILED.msg + "\n" + e.msg,
179 msg_code=messages.APT_UPDATE_FAILED.name,
180 )
181
182 return out
183
184
185def run_apt_install_command(
186 packages: List[str],
187 apt_options: Optional[List[str]] = None,
188 error_msg: Optional[str] = None,
189 env: Optional[Dict[str, str]] = {},
190) -> str:
191 if apt_options is None:
192 apt_options = []
193
194 try:
195 out = run_apt_command(
196 cmd=["apt-get", "install", "--assume-yes"]
197 + apt_options
198 + packages,
199 error_msg=error_msg,
200 env=env,
201 )
202 except exceptions.APTProcessConflictError:
203 raise exceptions.APTInstallProcessConflictError(header_msg=error_msg)
204 except exceptions.APTInvalidRepoError as e:
205 raise exceptions.APTInstallInvalidRepoError(
206 repo_msg=e.msg, header_msg=error_msg
207 )
154208
155 raise exceptions.UserFacingError(error_msg)
156 return out209 return out
157210
158211
@@ -181,7 +234,7 @@ def add_auth_apt_repo(
181 # Does this system have updates suite enabled?234 # Does this system have updates suite enabled?
182 updates_enabled = False235 updates_enabled = False
183 policy = run_apt_command(236 policy = run_apt_command(
184 ["apt-cache", "policy"], status.MESSAGE_APT_POLICY_FAILED237 ["apt-cache", "policy"], messages.APT_POLICY_FAILED.msg
185 )238 )
186 for line in policy.splitlines():239 for line in policy.splitlines():
187 # We only care about $suite-updates lines240 # We only care about $suite-updates lines
@@ -403,7 +456,7 @@ def setup_apt_proxy(
403 :return: None456 :return: None
404 """457 """
405 if http_proxy or https_proxy:458 if http_proxy or https_proxy:
406 print(status.MESSAGE_SETTING_SERVICE_PROXY.format(service="APT"))459 event.info(messages.SETTING_SERVICE_PROXY.format(service="APT"))
407460
408 apt_proxy_config = ""461 apt_proxy_config = ""
409 if http_proxy:462 if http_proxy:
@@ -413,9 +466,7 @@ def setup_apt_proxy(
413 proxy_url=https_proxy466 proxy_url=https_proxy
414 )467 )
415 if apt_proxy_config != "":468 if apt_proxy_config != "":
416 apt_proxy_config = (469 apt_proxy_config = messages.APT_PROXY_CONFIG_HEADER + apt_proxy_config
417 status.MESSAGE_APT_PROXY_CONFIG_HEADER + apt_proxy_config
418 )
419470
420 if apt_proxy_config == "":471 if apt_proxy_config == "":
421 util.remove_file(APT_PROXY_CONF_FILE)472 util.remove_file(APT_PROXY_CONF_FILE)
diff --git a/uaclient/cli.py b/uaclient/cli.py
index 340516c..6af7e74 100644
--- a/uaclient/cli.py
+++ b/uaclient/cli.py
@@ -15,8 +15,7 @@ import tempfile
15import textwrap15import textwrap
16import time16import time
17from functools import wraps17from functools import wraps
18from typing import Optional # noqa: F40118from typing import List, Optional, Tuple # noqa
19from typing import List, Tuple
2019
21import yaml20import yaml
2221
@@ -25,9 +24,11 @@ from uaclient import (
25 config,24 config,
26 contract,25 contract,
27 entitlements,26 entitlements,
27 event_logger,
28 exceptions,28 exceptions,
29 jobs,29 jobs,
30 lock,30 lock,
31 messages,
31 security,32 security,
32 security_status,33 security_status,
33)34)
@@ -35,6 +36,7 @@ from uaclient import status as ua_status
35from uaclient import util, version36from uaclient import util, version
36from uaclient.clouds import AutoAttachCloudInstance # noqa: F40137from uaclient.clouds import AutoAttachCloudInstance # noqa: F401
37from uaclient.clouds import identity38from uaclient.clouds import identity
39from uaclient.data_types import AttachActionsConfigFile, IncorrectTypeError
38from uaclient.defaults import (40from uaclient.defaults import (
39 CLOUD_BUILD_INFO,41 CLOUD_BUILD_INFO,
40 CONFIG_FIELD_ENVVAR_ALLOWLIST,42 CONFIG_FIELD_ENVVAR_ALLOWLIST,
@@ -82,6 +84,8 @@ UA_SERVICES = (
82 "ua-license-check.timer",84 "ua-license-check.timer",
83)85)
8486
87event = event_logger.get_event_logger()
88
8589
86class UAArgumentParser(argparse.ArgumentParser):90class UAArgumentParser(argparse.ArgumentParser):
87 def __init__(91 def __init__(
@@ -128,31 +132,31 @@ class UAArgumentParser(argparse.ArgumentParser):
128132
129 resources = contract.get_available_resources(config.UAConfig())133 resources = contract.get_available_resources(config.UAConfig())
130 for resource in resources:134 for resource in resources:
131 ent_cls = entitlements.entitlement_factory(resource["name"])135 try:
132 if ent_cls:136 ent_cls = entitlements.entitlement_factory(resource["name"])
133 # Because we don't know the presentation name if unattached137 except exceptions.EntitlementNotFoundError:
134 presentation_name = resource.get(138 continue
135 "presentedAs", resource["name"]139 # Because we don't know the presentation name if unattached
136 )140 presentation_name = resource.get("presentedAs", resource["name"])
137 if ent_cls.help_doc_url:141 if ent_cls.help_doc_url:
138 url = " ({})".format(ent_cls.help_doc_url)142 url = " ({})".format(ent_cls.help_doc_url)
139 else:143 else:
140 url = ""144 url = ""
141 service_info = textwrap.fill(145 service_info = textwrap.fill(
142 service_info_tmpl.format(146 service_info_tmpl.format(
143 name=presentation_name,147 name=presentation_name,
144 description=ent_cls.description,148 description=ent_cls.description,
145 url=url,149 url=url,
146 ),150 ),
147 width=PRINT_WRAP_WIDTH,151 width=PRINT_WRAP_WIDTH,
148 subsequent_indent=" ",152 subsequent_indent=" ",
149 break_long_words=False,153 break_long_words=False,
150 break_on_hyphens=False,154 break_on_hyphens=False,
151 )155 )
152 if ent_cls.is_beta:156 if ent_cls.is_beta:
153 beta_services_desc.append(service_info)157 beta_services_desc.append(service_info)
154 else:158 else:
155 non_beta_services_desc.append(service_info)159 non_beta_services_desc.append(service_info)
156160
157 return (non_beta_services_desc, beta_services_desc)161 return (non_beta_services_desc, beta_services_desc)
158162
@@ -179,7 +183,25 @@ def assert_root(f):
179 def new_f(*args, **kwargs):183 def new_f(*args, **kwargs):
180 if os.getuid() != 0:184 if os.getuid() != 0:
181 raise exceptions.NonRootUserError()185 raise exceptions.NonRootUserError()
182 return f(*args, **kwargs)186 else:
187 return f(*args, **kwargs)
188
189 return new_f
190
191
192def verify_json_format_args(f):
193 """Decorator to verify if correct params are used for json format"""
194
195 @wraps(f)
196 def new_f(cmd_args, *args, **kwargs):
197 if not cmd_args:
198 return f(cmd_args, *args, **kwargs)
199
200 if cmd_args.format == "json" and not cmd_args.assume_yes:
201 msg = messages.JSON_FORMAT_REQUIRE_ASSUME_YES
202 raise exceptions.UserFacingError(msg=msg.msg, msg_code=msg.name)
203 else:
204 return f(cmd_args, *args, **kwargs)
183205
184 return new_f206 return new_f
185207
@@ -352,6 +374,21 @@ def attach_parser(parser):
352 dest="auto_enable",374 dest="auto_enable",
353 help="do not enable any recommended services automatically",375 help="do not enable any recommended services automatically",
354 )376 )
377 parser.add_argument(
378 "--attach-config",
379 type=argparse.FileType("r"),
380 help=(
381 "use the provided attach config file instead of passing the token"
382 " on the cli"
383 ),
384 )
385 parser.add_argument(
386 "--format",
387 action="store",
388 choices=["cli", "json"],
389 default="cli",
390 help=("output enable in the specified format (default: cli)"),
391 )
355 return parser392 return parser
356393
357394
@@ -389,15 +426,6 @@ def security_status_parser(parser):
389 choices=("json", "yaml"),426 choices=("json", "yaml"),
390 required=True,427 required=True,
391 )428 )
392 parser.add_argument(
393 "--beta",
394 help=(
395 "Acknowledge that this output is not final and may change in the"
396 " next version"
397 ),
398 action="store_true",
399 required=True,
400 )
401 return parser429 return parser
402430
403431
@@ -465,6 +493,13 @@ def detach_parser(parser):
465 action="store_true",493 action="store_true",
466 help="do not prompt for confirmation before performing the detach",494 help="do not prompt for confirmation before performing the detach",
467 )495 )
496 parser.add_argument(
497 "--format",
498 action="store",
499 choices=["cli", "json"],
500 default="cli",
501 help=("output enable in the specified format (default: cli)"),
502 )
468 return parser503 return parser
469504
470505
@@ -534,6 +569,13 @@ def enable_parser(parser):
534 parser.add_argument(569 parser.add_argument(
535 "--beta", action="store_true", help="allow beta service to be enabled"570 "--beta", action="store_true", help="allow beta service to be enabled"
536 )571 )
572 parser.add_argument(
573 "--format",
574 action="store",
575 choices=["cli", "json"],
576 default="cli",
577 help=("output enable in the specified format (default: cli)"),
578 )
537 return parser579 return parser
538580
539581
@@ -561,6 +603,13 @@ def disable_parser(parser):
561 action="store_true",603 action="store_true",
562 help="do not prompt for confirmation before performing the disable",604 help="do not prompt for confirmation before performing the disable",
563 )605 )
606 parser.add_argument(
607 "--format",
608 action="store",
609 choices=["cli", "json"],
610 default="cli",
611 help=("output disable in the specified format (default: cli)"),
612 )
564 return parser613 return parser
565614
566615
@@ -643,7 +692,7 @@ def status_parser(parser):
643 return parser692 return parser
644693
645694
646def _perform_disable(entitlement_name, cfg, *, assume_yes):695def _perform_disable(entitlement, cfg, *, assume_yes, update_status=True):
647 """Perform the disable action on a named entitlement.696 """Perform the disable action on a named entitlement.
648697
649 :param entitlement_name: the name of the entitlement to enable698 :param entitlement_name: the name of the entitlement to enable
@@ -653,10 +702,27 @@ def _perform_disable(entitlement_name, cfg, *, assume_yes):
653702
654 @return: True on success, False otherwise703 @return: True on success, False otherwise
655 """704 """
656 ent_cls = entitlements.entitlement_factory(entitlement_name)705 ret, reason = entitlement.disable()
657 entitlement = ent_cls(cfg, assume_yes=assume_yes)706
658 ret = entitlement.disable()707 if not ret:
659 cfg.status() # Update the status cache708 event.service_failed(entitlement.name)
709
710 if reason is not None and isinstance(
711 reason, ua_status.CanDisableFailure
712 ):
713 if reason.message is not None:
714 event.info(reason.message.msg)
715 event.error(
716 error_msg=reason.message.msg,
717 error_code=reason.message.name,
718 service=entitlement.name,
719 )
720 else:
721 event.service_processed(entitlement.name)
722
723 if update_status:
724 cfg.status() # Update the status cache
725
660 return ret726 return ret
661727
662728
@@ -709,7 +775,7 @@ def action_config_show(args, *, cfg, **kwargs):
709 raise exceptions.UserFacingError(775 raise exceptions.UserFacingError(
710 textwrap.fill(776 textwrap.fill(
711 msg,777 msg,
712 width=ua_status.PRINT_WRAP_WIDTH,778 width=PRINT_WRAP_WIDTH,
713 subsequent_indent=" " * indent_position,779 subsequent_indent=" " * indent_position,
714 )780 )
715 )781 )
@@ -844,8 +910,9 @@ def action_config_unset(args, *, cfg, **kwargs):
844 return 0910 return 0
845911
846912
913@verify_json_format_args
847@assert_root914@assert_root
848@assert_attached(ua_status.MESSAGE_ENABLE_FAILURE_UNATTACHED_TMPL)915@assert_attached(messages.ENABLE_FAILURE_UNATTACHED)
849@assert_lock_file("ua disable")916@assert_lock_file("ua disable")
850def action_disable(args, *, cfg, **kwargs):917def action_disable(args, *, cfg, **kwargs):
851 """Perform the disable action on a list of entitlements.918 """Perform the disable action on a list of entitlements.
@@ -856,11 +923,13 @@ def action_disable(args, *, cfg, **kwargs):
856 entitlements_found, entitlements_not_found = get_valid_entitlement_names(923 entitlements_found, entitlements_not_found = get_valid_entitlement_names(
857 names924 names
858 )925 )
859 tmpl = ua_status.MESSAGE_INVALID_SERVICE_OP_FAILURE_TMPL
860 ret = True926 ret = True
861927
862 for entitlement in entitlements_found:928 for ent_name in entitlements_found:
863 ret &= _perform_disable(entitlement, cfg, assume_yes=args.assume_yes)929 ent_cls = entitlements.entitlement_factory(ent_name)
930 ent = ent_cls(cfg, assume_yes=args.assume_yes)
931
932 ret &= _perform_disable(ent, cfg, assume_yes=args.assume_yes)
864933
865 if entitlements_not_found:934 if entitlements_not_found:
866 valid_names = (935 valid_names = (
@@ -876,50 +945,68 @@ def action_disable(args, *, cfg, **kwargs):
876 break_on_hyphens=False,945 break_on_hyphens=False,
877 )946 )
878 )947 )
879 raise exceptions.UserFacingError(948 raise exceptions.InvalidServiceToDisableError(
880 tmpl.format(949 operation="disable",
881 operation="disable",950 name=", ".join(entitlements_not_found),
882 name=", ".join(entitlements_not_found),951 service_msg=service_msg,
883 service_msg=service_msg,
884 )
885 )952 )
886953
954 event.process_events()
887 return 0 if ret else 1955 return 0 if ret else 1
888956
889957
958def _create_enable_entitlements_not_found_message(
959 entitlements_not_found, *, allow_beta: bool
960) -> messages.NamedMessage:
961 """
962 Constructs the MESSAGE_INVALID_SERVICE_OP_FAILURE message
963 based on the attempted services and valid services.
964 """
965 valid_services_names = entitlements.valid_services(allow_beta=allow_beta)
966 valid_names = ", ".join(valid_services_names)
967 service_msg = "\n".join(
968 textwrap.wrap(
969 "Try " + valid_names + ".",
970 width=80,
971 break_long_words=False,
972 break_on_hyphens=False,
973 )
974 )
975
976 return messages.INVALID_SERVICE_OP_FAILURE.format(
977 operation="enable",
978 name=", ".join(entitlements_not_found),
979 service_msg=service_msg,
980 )
981
982
983@verify_json_format_args
890@assert_root984@assert_root
891@assert_attached(ua_status.MESSAGE_ENABLE_FAILURE_UNATTACHED_TMPL)985@assert_attached(messages.ENABLE_FAILURE_UNATTACHED)
892@assert_lock_file("ua enable")986@assert_lock_file("ua enable")
893def action_enable(args, *, cfg, **kwargs):987def action_enable(args, *, cfg, **kwargs):
894 """Perform the enable action on a named entitlement.988 """Perform the enable action on a named entitlement.
895989
896 @return: 0 on success, 1 otherwise990 @return: 0 on success, 1 otherwise
897 """991 """
898 print(ua_status.MESSAGE_REFRESH_CONTRACT_ENABLE)992 event.info(messages.REFRESH_CONTRACT_ENABLE)
899 try:993 try:
900 contract.request_updated_contract(cfg)994 contract.request_updated_contract(cfg)
901 except (util.UrlError, exceptions.UserFacingError):995 except (exceptions.UrlError, exceptions.UserFacingError):
902 # Inability to refresh is not a critical issue during enable996 # Inability to refresh is not a critical issue during enable
903 logging.debug(997 logging.debug(messages.REFRESH_CONTRACT_FAILURE, exc_info=True)
904 ua_status.MESSAGE_REFRESH_CONTRACT_FAILURE, exc_info=True998 event.warning(warning_msg=messages.REFRESH_CONTRACT_FAILURE)
905 )
906999
907 names = getattr(args, "service", [])1000 names = getattr(args, "service", [])
908 entitlements_found, entitlements_not_found = get_valid_entitlement_names(1001 entitlements_found, entitlements_not_found = get_valid_entitlement_names(
909 names1002 names
910 )1003 )
911 valid_services_names = entitlements.valid_services(allow_beta=args.beta)
912 ret = True1004 ret = True
913 for ent_name in entitlements_found:1005 for ent_name in entitlements_found:
914 try:1006 try:
915 ent_cls = entitlements.entitlement_factory(ent_name)1007 ent_ret, reason = actions.enable_entitlement_by_name(
916 entitlement = ent_cls(1008 cfg, ent_name, assume_yes=args.assume_yes, allow_beta=args.beta
917 cfg,
918 assume_yes=args.assume_yes,
919 allow_beta=args.beta,
920 called_name=ent_name,
921 )1009 )
922 ent_ret, reason = entitlement.enable()
923 cfg.status() # Update the status cache1010 cfg.status() # Update the status cache
9241011
925 if (1012 if (
@@ -928,39 +1015,41 @@ def action_enable(args, *, cfg, **kwargs):
928 and isinstance(reason, ua_status.CanEnableFailure)1015 and isinstance(reason, ua_status.CanEnableFailure)
929 ):1016 ):
930 if reason.message is not None:1017 if reason.message is not None:
931 print(reason.message)1018 event.info(reason.message.msg)
1019 event.error(
1020 error_msg=reason.message.msg,
1021 error_code=reason.message.name,
1022 service=ent_name,
1023 )
932 if reason.reason == ua_status.CanEnableFailureReason.IS_BETA:1024 if reason.reason == ua_status.CanEnableFailureReason.IS_BETA:
933 # if we failed because ent is in beta and there was no1025 # if we failed because ent is in beta and there was no
934 # allow_beta flag/config, pretend it doesn't exist1026 # allow_beta flag/config, pretend it doesn't exist
935 entitlements_not_found.append(ent_name)1027 entitlements_not_found.append(ent_name)
1028 elif ent_ret:
1029 event.service_processed(service=ent_name)
1030 elif not ent_ret and reason is None:
1031 event.service_failed(service=ent_name)
9361032
937 ret &= ent_ret1033 ret &= ent_ret
938 except exceptions.UserFacingError as e:1034 except exceptions.UserFacingError as e:
939 print(e)1035 event.info(e.msg)
1036 event.error(
1037 error_msg=e.msg, error_code=e.msg_code, service=ent_name
1038 )
940 ret = False1039 ret = False
9411040
942 if entitlements_not_found:1041 if entitlements_not_found:
943 valid_names = ", ".join(valid_services_names)1042 msg = _create_enable_entitlements_not_found_message(
944 service_msg = "\n".join(1043 entitlements_not_found, allow_beta=args.beta
945 textwrap.wrap(
946 "Try " + valid_names + ".",
947 width=80,
948 break_long_words=False,
949 break_on_hyphens=False,
950 )
951 )
952 tmpl = ua_status.MESSAGE_INVALID_SERVICE_OP_FAILURE_TMPL
953 raise exceptions.UserFacingError(
954 tmpl.format(
955 operation="enable",
956 name=", ".join(entitlements_not_found),
957 service_msg=service_msg,
958 )
959 )1044 )
1045 event.services_failed(entitlements_not_found)
1046 raise exceptions.UserFacingError(msg=msg.msg, msg_code=msg.name)
9601047
1048 event.process_events()
961 return 0 if ret else 11049 return 0 if ret else 1
9621050
9631051
1052@verify_json_format_args
964@assert_root1053@assert_root
965@assert_attached()1054@assert_attached()
966@assert_lock_file("ua detach")1055@assert_lock_file("ua detach")
@@ -985,11 +1074,15 @@ def _detach(cfg: config.UAConfig, assume_yes: bool) -> int:
985 to_disable = []1074 to_disable = []
986 for ent_cls in entitlements.ENTITLEMENT_CLASSES:1075 for ent_cls in entitlements.ENTITLEMENT_CLASSES:
987 ent = ent_cls(cfg=cfg, assume_yes=assume_yes)1076 ent = ent_cls(cfg=cfg, assume_yes=assume_yes)
988 if ent.can_disable(silent=True):1077 # For detach, we should not consider that a service
1078 # cannot be disabled because of dependent services,
1079 # since we are going to disable all of them anyway
1080 ret, _ = ent.can_disable(ignore_dependent_services=True)
1081 if ret:
989 to_disable.append(ent)1082 to_disable.append(ent)
9901083
991 """1084 """
992 We will nake sure that services without dependencies are disabled first1085 We will make sure that services without dependencies are disabled first
993 PS: This will only work because we have only three services with reverse1086 PS: This will only work because we have only three services with reverse
994 dependencies:1087 dependencies:
995 * ros: ros-updates1088 * ros: ros-updates
@@ -1007,40 +1100,47 @@ def _detach(cfg: config.UAConfig, assume_yes: bool) -> int:
10071100
1008 if to_disable:1101 if to_disable:
1009 suffix = "s" if len(to_disable) > 1 else ""1102 suffix = "s" if len(to_disable) > 1 else ""
1010 print("Detach will disable the following service{}:".format(suffix))1103 event.info(
1104 "Detach will disable the following service{}:".format(suffix)
1105 )
1011 for ent in to_disable:1106 for ent in to_disable:
1012 print(" {}".format(ent.name))1107 event.info(" {}".format(ent.name))
1013 if not util.prompt_for_confirmation(assume_yes=assume_yes):1108 if not util.prompt_for_confirmation(assume_yes=assume_yes):
1014 return 11109 return 1
1015 for ent in to_disable:1110 for ent in to_disable:
1016 ent.disable(silent=False)1111 _perform_disable(ent, cfg, assume_yes=assume_yes, update_status=False)
1017 contract_client = contract.UAContractClient(cfg)1112
1018 machine_token = cfg.machine_token["machineToken"]
1019 contract_id = cfg.machine_token["machineTokenInfo"]["contractInfo"]["id"]
1020 contract_client.detach_machine_from_contract(machine_token, contract_id)
1021 cfg.delete_cache()1113 cfg.delete_cache()
1022 jobs.enable_license_check_if_applicable(cfg)1114 jobs.enable_license_check_if_applicable(cfg)
1023 update_apt_and_motd_messages(cfg)1115 update_apt_and_motd_messages(cfg)
1024 print(ua_status.MESSAGE_DETACH_SUCCESS)1116 event.info(messages.DETACH_SUCCESS)
1117 event.process_events()
1025 return 01118 return 0
10261119
10271120
1028def _post_cli_attach(cfg: config.UAConfig) -> None:1121def _post_cli_attach(cfg: config.UAConfig) -> None:
1029 contract_name = cfg.machine_token["machineTokenInfo"]["contractInfo"][1122 contract_name = None
1030 "name"1123
1031 ]1124 if cfg.machine_token:
1125 contract_name = (
1126 cfg.machine_token.get("machineTokenInfo", {})
1127 .get("contractInfo", {})
1128 .get("name")
1129 )
10321130
1033 if contract_name:1131 if contract_name:
1034 print(1132 event.info(
1035 ua_status.MESSAGE_ATTACH_SUCCESS_TMPL.format(1133 messages.ATTACH_SUCCESS_TMPL.format(contract_name=contract_name)
1036 contract_name=contract_name
1037 )
1038 )1134 )
1039 else:1135 else:
1040 print(ua_status.MESSAGE_ATTACH_SUCCESS_NO_CONTRACT_NAME)1136 event.info(messages.ATTACH_SUCCESS_NO_CONTRACT_NAME)
10411137
1042 jobs.disable_license_check_if_applicable(cfg)1138 jobs.disable_license_check_if_applicable(cfg)
1043 action_status(args=None, cfg=cfg)1139
1140 status, _ret = actions.status(cfg)
1141 output = ua_status.format_tabular(status)
1142 event.info(util.handle_unicode_characters(output))
1143 event.process_events()
10441144
10451145
1046@assert_root1146@assert_root
@@ -1064,27 +1164,25 @@ def action_auto_attach(args, *, cfg):
1064 raise exceptions.AlreadyAttachedError(cfg)1164 raise exceptions.AlreadyAttachedError(cfg)
1065 if isinstance(e, exceptions.CloudFactoryNoCloudError):1165 if isinstance(e, exceptions.CloudFactoryNoCloudError):
1066 raise exceptions.UserFacingError(1166 raise exceptions.UserFacingError(
1067 ua_status.MESSAGE_UNABLE_TO_DETERMINE_CLOUD_TYPE1167 messages.UNABLE_TO_DETERMINE_CLOUD_TYPE
1068 )1168 )
1069 if isinstance(e, exceptions.CloudFactoryNonViableCloudError):1169 if isinstance(e, exceptions.CloudFactoryNonViableCloudError):
1070 raise exceptions.UserFacingError(1170 raise exceptions.UserFacingError(messages.UNSUPPORTED_AUTO_ATTACH)
1071 ua_status.MESSAGE_UNSUPPORTED_AUTO_ATTACH
1072 )
1073 if isinstance(e, exceptions.CloudFactoryUnsupportedCloudError):1171 if isinstance(e, exceptions.CloudFactoryUnsupportedCloudError):
1074 raise exceptions.NonAutoAttachImageError(1172 raise exceptions.NonAutoAttachImageError(
1075 ua_status.MESSAGE_UNSUPPORTED_AUTO_ATTACH_CLOUD_TYPE.format(1173 messages.UNSUPPORTED_AUTO_ATTACH_CLOUD_TYPE.format(
1076 cloud_type=e.cloud_type1174 cloud_type=e.cloud_type
1077 )1175 )
1078 )1176 )
1079 # we shouldn't get here, but this is a reasonable default just in case1177 # we shouldn't get here, but this is a reasonable default just in case
1080 raise exceptions.UserFacingError(1178 raise exceptions.UserFacingError(
1081 ua_status.MESSAGE_UNABLE_TO_DETERMINE_CLOUD_TYPE1179 messages.UNABLE_TO_DETERMINE_CLOUD_TYPE
1082 )1180 )
10831181
1084 if not instance:1182 if not instance:
1085 # we shouldn't get here, but this is a reasonable default just in case1183 # we shouldn't get here, but this is a reasonable default just in case
1086 raise exceptions.UserFacingError(1184 raise exceptions.UserFacingError(
1087 ua_status.MESSAGE_UNABLE_TO_DETERMINE_CLOUD_TYPE1185 messages.UNABLE_TO_DETERMINE_CLOUD_TYPE
1088 )1186 )
10891187
1090 current_iid = identity.get_instance_id()1188 current_iid = identity.get_instance_id()
@@ -1095,13 +1193,13 @@ def action_auto_attach(args, *, cfg):
1095 print("Re-attaching Ubuntu Advantage subscription on new instance")1193 print("Re-attaching Ubuntu Advantage subscription on new instance")
1096 if _detach(cfg, assume_yes=True) != 0:1194 if _detach(cfg, assume_yes=True) != 0:
1097 raise exceptions.UserFacingError(1195 raise exceptions.UserFacingError(
1098 ua_status.MESSAGE_DETACH_AUTOMATION_FAILURE1196 messages.DETACH_AUTOMATION_FAILURE
1099 )1197 )
11001198
1101 try:1199 try:
1102 actions.auto_attach(cfg, instance)1200 actions.auto_attach(cfg, instance)
1103 except util.UrlError:1201 except exceptions.UrlError:
1104 print(ua_status.MESSAGE_ATTACH_FAILURE)1202 event.info(messages.ATTACH_FAILURE)
1105 return 11203 return 1
1106 except exceptions.UserFacingError:1204 except exceptions.UserFacingError:
1107 return 11205 return 1
@@ -1114,22 +1212,83 @@ def action_auto_attach(args, *, cfg):
1114@assert_root1212@assert_root
1115@assert_lock_file("ua attach")1213@assert_lock_file("ua attach")
1116def action_attach(args, *, cfg):1214def action_attach(args, *, cfg):
1117 if not args.token:1215 if not args.token and not args.attach_config:
1118 raise exceptions.UserFacingError(1216 raise exceptions.UserFacingError(
1119 ua_status.MESSAGE_ATTACH_REQUIRES_TOKEN1217 msg=messages.ATTACH_REQUIRES_TOKEN.msg,
1218 msg_code=messages.ATTACH_REQUIRES_TOKEN.name,
1120 )1219 )
1121 try:1220 if args.token and args.attach_config:
1122 actions.attach_with_token(1221 raise exceptions.UserFacingError(
1123 cfg, token=args.token, allow_enable=args.auto_enable1222 msg=messages.ATTACH_TOKEN_ARG_XOR_CONFIG.msg,
1223 msg_code=messages.ATTACH_TOKEN_ARG_XOR_CONFIG.name,
1124 )1224 )
1125 except util.UrlError:1225
1126 print(ua_status.MESSAGE_ATTACH_FAILURE)1226 if args.token:
1227 token = args.token
1228 enable_services_override = None
1229 else:
1230 try:
1231 attach_config = AttachActionsConfigFile.from_dict(
1232 yaml.safe_load(args.attach_config)
1233 )
1234 except IncorrectTypeError as e:
1235 raise exceptions.AttachInvalidConfigFileError(
1236 config_name=args.attach_config.name, error=e.msg
1237 )
1238
1239 token = attach_config.token
1240 enable_services_override = attach_config.enable_services
1241
1242 allow_enable = args.auto_enable and enable_services_override is None
1243
1244 try:
1245 actions.attach_with_token(cfg, token=token, allow_enable=allow_enable)
1246 except exceptions.UrlError:
1247 msg = messages.ATTACH_FAILURE
1248 event.info(msg.msg)
1249 event.error(error_msg=msg.msg, error_code=msg.name)
1250 event.process_events()
1127 return 11251 return 1
1128 except exceptions.UserFacingError:1252 except exceptions.UserFacingError as exc:
1253 event.info(exc.msg)
1254 event.error(error_msg=exc.msg, error_code=exc.msg_code)
1255 event.process_events()
1129 return 11256 return 1
1130 else:1257 else:
1258 ret = 0
1259 if enable_services_override is not None and args.auto_enable:
1260 found, not_found = get_valid_entitlement_names(
1261 enable_services_override
1262 )
1263 for name in found:
1264 ent_ret, reason = actions.enable_entitlement_by_name(
1265 cfg, name, assume_yes=True, allow_beta=True
1266 )
1267 if not ent_ret:
1268 ret = 1
1269 if (
1270 reason is not None
1271 and isinstance(reason, ua_status.CanEnableFailure)
1272 and reason.message is not None
1273 ):
1274 event.info(reason.message.msg)
1275 event.error(
1276 error_msg=reason.message.msg,
1277 error_code=reason.message.name,
1278 service=name,
1279 )
1280 else:
1281 event.service_processed(name)
1282
1283 if not_found:
1284 msg = _create_enable_entitlements_not_found_message(
1285 not_found, allow_beta=True
1286 )
1287 event.info(msg.msg, file_type=sys.stderr)
1288 event.error(error_msg=msg.msg, error_code=msg.name)
1289 ret = 1
1131 _post_cli_attach(cfg)1290 _post_cli_attach(cfg)
1132 return 01291 return ret
11331292
11341293
1135def _write_command_output_to_file(1294def _write_command_output_to_file(
@@ -1138,7 +1297,7 @@ def _write_command_output_to_file(
1138 """Helper which runs a command and writes output or error to filename."""1297 """Helper which runs a command and writes output or error to filename."""
1139 try:1298 try:
1140 out, _ = util.subp(cmd.split(), rcs=return_codes)1299 out, _ = util.subp(cmd.split(), rcs=return_codes)
1141 except util.ProcessExecutionError as e:1300 except exceptions.ProcessExecutionError as e:
1142 util.write_file("{}-error".format(filename), str(e))1301 util.write_file("{}-error".format(filename), str(e))
1143 else:1302 else:
1144 util.write_file(filename, out)1303 util.write_file(filename, out)
@@ -1326,26 +1485,27 @@ def action_status(args, *, cfg):
1326 cfg = config.UAConfig()1485 cfg = config.UAConfig()
1327 show_beta = args.all if args else False1486 show_beta = args.all if args else False
1328 token = args.simulate_with_token if args else None1487 token = args.simulate_with_token if args else None
1329 if token:
1330 status = cfg.simulate_status(token=token, show_beta=show_beta)
1331 else:
1332 status = cfg.status(show_beta=show_beta)
1333 active_value = ua_status.UserFacingConfigStatus.ACTIVE.value1488 active_value = ua_status.UserFacingConfigStatus.ACTIVE.value
1489
1490 status, ret = actions.status(
1491 cfg, simulate_with_token=token, show_beta=show_beta
1492 )
1334 config_active = bool(status["execution_status"] == active_value)1493 config_active = bool(status["execution_status"] == active_value)
1494
1335 if args and args.wait and config_active:1495 if args and args.wait and config_active:
1336 while status["execution_status"] == active_value:1496 while status["execution_status"] == active_value:
1337 print(".", end="")1497 event.info(".", end="")
1338 time.sleep(1)1498 time.sleep(1)
1339 status = cfg.status(show_beta=show_beta)1499 status, ret = actions.status(
1340 print("")1500 cfg, simulate_with_token=token, show_beta=show_beta
1341 if args and args.format == "json":1501 )
1342 print(ua_status.format_json_status(status))1502 event.info("")
1343 elif args and args.format == "yaml":1503
1344 print(ua_status.format_yaml_status(status))1504 event.set_output_content(status)
1345 else:1505 output = ua_status.format_tabular(status)
1346 output = ua_status.format_tabular(status)1506 event.info(util.handle_unicode_characters(output))
1347 print(util.handle_unicode_characters(output))1507 event.process_events()
1348 return 01508 return ret
13491509
13501510
1351def get_version(_args=None, _cfg=None):1511def get_version(_args=None, _cfg=None):
@@ -1365,23 +1525,19 @@ def _action_refresh_config(args, cfg: config.UAConfig):
1365 except RuntimeError as exc:1525 except RuntimeError as exc:
1366 with util.disable_log_to_console():1526 with util.disable_log_to_console():
1367 logging.exception(exc)1527 logging.exception(exc)
1368 raise exceptions.UserFacingError(1528 raise exceptions.UserFacingError(messages.REFRESH_CONFIG_FAILURE)
1369 ua_status.MESSAGE_REFRESH_CONFIG_FAILURE1529 print(messages.REFRESH_CONFIG_SUCCESS)
1370 )
1371 print(ua_status.MESSAGE_REFRESH_CONFIG_SUCCESS)
13721530
13731531
1374@assert_attached()1532@assert_attached()
1375def _action_refresh_contract(_args, cfg: config.UAConfig):1533def _action_refresh_contract(_args, cfg: config.UAConfig):
1376 try:1534 try:
1377 contract.request_updated_contract(cfg)1535 contract.request_updated_contract(cfg)
1378 except util.UrlError as exc:1536 except exceptions.UrlError as exc:
1379 with util.disable_log_to_console():1537 with util.disable_log_to_console():
1380 logging.exception(exc)1538 logging.exception(exc)
1381 raise exceptions.UserFacingError(1539 raise exceptions.UserFacingError(messages.REFRESH_CONTRACT_FAILURE)
1382 ua_status.MESSAGE_REFRESH_CONTRACT_FAILURE1540 print(messages.REFRESH_CONTRACT_SUCCESS)
1383 )
1384 print(ua_status.MESSAGE_REFRESH_CONTRACT_SUCCESS)
13851541
13861542
1387@assert_root1543@assert_root
@@ -1444,8 +1600,10 @@ def setup_logging(console_level, log_level, log_file=None, logger=None):
1444 if os.getuid() == 0:1600 if os.getuid() == 0:
1445 # Setup readable-by-root-only debug file logging if running as root1601 # Setup readable-by-root-only debug file logging if running as root
1446 log_file_path = pathlib.Path(log_file)1602 log_file_path = pathlib.Path(log_file)
1447 log_file_path.touch()1603
1448 log_file_path.chmod(0o600)1604 if not log_file_path.exists():
1605 log_file_path.touch()
1606 log_file_path.chmod(0o644)
14491607
1450 file_handler = logging.FileHandler(log_file)1608 file_handler = logging.FileHandler(log_file)
1451 file_handler.setLevel(log_level)1609 file_handler.setLevel(log_level)
@@ -1454,6 +1612,17 @@ def setup_logging(console_level, log_level, log_file=None, logger=None):
1454 logger.addHandler(file_handler)1612 logger.addHandler(file_handler)
14551613
14561614
1615def set_event_mode(cmd_args):
1616 """Set the right event mode based on the args provided"""
1617 if cmd_args.command in ("attach", "detach", "enable", "disable", "status"):
1618 event.set_command(cmd_args.command)
1619 if hasattr(cmd_args, "format"):
1620 if cmd_args.format == "json":
1621 event.set_event_mode(event_logger.EventLoggerMode.JSON)
1622 if cmd_args.format == "yaml":
1623 event.set_event_mode(event_logger.EventLoggerMode.YAML)
1624
1625
1457def main_error_handler(func):1626def main_error_handler(func):
1458 def wrapper(*args, **kwargs):1627 def wrapper(*args, **kwargs):
1459 try:1628 try:
@@ -1464,40 +1633,53 @@ def main_error_handler(func):
1464 print("Interrupt received; exiting.", file=sys.stderr)1633 print("Interrupt received; exiting.", file=sys.stderr)
1465 lock.clear_lock_file_if_present()1634 lock.clear_lock_file_if_present()
1466 sys.exit(1)1635 sys.exit(1)
1467 except util.UrlError as exc:1636 except exceptions.UrlError as exc:
1468 if "CERTIFICATE_VERIFY_FAILED" in str(exc):1637 if "CERTIFICATE_VERIFY_FAILED" in str(exc):
1469 tmpl = ua_status.MESSAGE_SSL_VERIFICATION_ERROR_CA_CERTIFICATES1638 tmpl = messages.SSL_VERIFICATION_ERROR_CA_CERTIFICATES
1470 if util.is_installed("ca-certificates"):1639 if util.is_installed("ca-certificates"):
1471 tmpl = (1640 tmpl = messages.SSL_VERIFICATION_ERROR_OPENSSL_CONFIG
1472 ua_status.MESSAGE_SSL_VERIFICATION_ERROR_OPENSSL_CONFIG1641 msg = tmpl.format(url=exc.url)
1473 )1642 event.error(error_msg=msg.msg, error_code=msg.name)
1474 print(tmpl.format(url=exc.url), file=sys.stderr)1643 event.info(info_msg=msg.msg, file_type=sys.stderr)
1475 else:1644 else:
1476 with util.disable_log_to_console():1645 with util.disable_log_to_console():
1477 msg_args = {"url": exc.url, "error": exc}1646 msg_args = {"url": exc.url, "error": exc}
1478 if exc.url:1647 if exc.url:
1479 msg_tmpl = (1648 msg_tmpl = (
1480 ua_status.LOG_CONNECTIVITY_ERROR_WITH_URL_TMPL1649 messages.LOG_CONNECTIVITY_ERROR_WITH_URL_TMPL
1481 )1650 )
1482 else:1651 else:
1483 msg_tmpl = ua_status.LOG_CONNECTIVITY_ERROR_TMPL1652 msg_tmpl = messages.LOG_CONNECTIVITY_ERROR_TMPL
1484 logging.exception(msg_tmpl.format(**msg_args))1653 logging.exception(msg_tmpl.format(**msg_args))
1485 print(ua_status.MESSAGE_CONNECTIVITY_ERROR, file=sys.stderr)1654
1655 msg = messages.CONNECTIVITY_ERROR
1656 event.error(error_msg=msg.msg, error_code=msg.name)
1657 event.info(info_msg=msg.msg, file_type=sys.stderr)
1658
1486 lock.clear_lock_file_if_present()1659 lock.clear_lock_file_if_present()
1660 event.process_events()
1487 sys.exit(1)1661 sys.exit(1)
1488 except exceptions.UserFacingError as exc:1662 except exceptions.UserFacingError as exc:
1489 with util.disable_log_to_console():1663 with util.disable_log_to_console():
1490 logging.error(exc.msg)1664 logging.error(exc.msg)
1491 print("{}".format(exc.msg), file=sys.stderr)1665 event.error(error_msg=exc.msg, error_code=exc.msg_code)
1666 event.info(info_msg="{}".format(exc.msg), file_type=sys.stderr)
1492 if not isinstance(exc, exceptions.LockHeldError):1667 if not isinstance(exc, exceptions.LockHeldError):
1493 # Only clear the lock if it is ours.1668 # Only clear the lock if it is ours.
1494 lock.clear_lock_file_if_present()1669 lock.clear_lock_file_if_present()
1670 event.process_events()
1495 sys.exit(exc.exit_code)1671 sys.exit(exc.exit_code)
1496 except Exception:1672 except Exception as e:
1497 with util.disable_log_to_console():1673 with util.disable_log_to_console():
1498 logging.exception("Unhandled exception, please file a bug")1674 logging.exception("Unhandled exception, please file a bug")
1499 lock.clear_lock_file_if_present()1675 lock.clear_lock_file_if_present()
1500 print(ua_status.MESSAGE_UNEXPECTED_ERROR, file=sys.stderr)1676 event.info(
1677 info_msg=messages.UNEXPECTED_ERROR.msg, file_type=sys.stderr
1678 )
1679 event.error(
1680 error_msg=getattr(e, "msg", str(e)), error_type="exception"
1681 )
1682 event.process_events()
1501 sys.exit(1)1683 sys.exit(1)
15021684
1503 return wrapper1685 return wrapper
@@ -1514,6 +1696,7 @@ def main(sys_argv=None):
1514 print("Try 'ua --help' for more information.")1696 print("Try 'ua --help' for more information.")
1515 sys.exit(1)1697 sys.exit(1)
1516 args = parser.parse_args(args=cli_arguments)1698 args = parser.parse_args(args=cli_arguments)
1699 set_event_mode(args)
1517 cfg = config.UAConfig()1700 cfg = config.UAConfig()
15181701
1519 http_proxy = cfg.http_proxy1702 http_proxy = cfg.http_proxy
diff --git a/uaclient/clouds/gcp.py b/uaclient/clouds/gcp.py
index 55f518c..c520d47 100644
--- a/uaclient/clouds/gcp.py
+++ b/uaclient/clouds/gcp.py
@@ -20,6 +20,7 @@ GCP_LICENSES = {
20 "xenial": "8045211386737108299",20 "xenial": "8045211386737108299",
21 "bionic": "6022427724719891830",21 "bionic": "6022427724719891830",
22 "focal": "599959289349842382",22 "focal": "599959289349842382",
23 "jammy": "2592866803419978320",
23}24}
2425
2526
diff --git a/uaclient/clouds/identity.py b/uaclient/clouds/identity.py
index cd8f7b4..7542d35 100644
--- a/uaclient/clouds/identity.py
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: