Merge ~chad.smith/ubuntu/+source/ubuntu-advantage-tools:upload-release-26 into ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel

Proposed by Chad Smith
Status: Merged
Merged at revision: 83975b05565e50fa8fc8efc315c7d09babbd5f0a
Proposed branch: ~chad.smith/ubuntu/+source/ubuntu-advantage-tools:upload-release-26
Merge into: ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel
Diff against target: 18644 lines (+13502/-1203)
107 files modified
.github/PULL_REQUEST_TEMPLATE.md (+33/-0)
.gitignore (+24/-0)
Jenkinsfile (+263/-0)
Makefile (+7/-1)
README.md (+277/-23)
RELEASES.md (+63/-0)
apt-hook/hook.cc (+61/-22)
debian/changelog (+225/-0)
debian/control (+4/-3)
debian/po/POTFILES.in (+1/-0)
debian/po/templates.pot (+36/-0)
debian/postinst (+112/-10)
debian/postrm (+4/-5)
debian/prerm (+2/-2)
debian/rules (+5/-7)
debian/ubuntu-advantage-tools.templates (+1/-1)
dev/null (+0/-7)
features/attach_invalidtoken.feature (+12/-5)
features/attach_validtoken.feature (+354/-9)
features/attached_commands.feature (+382/-53)
features/attached_enable.feature (+373/-67)
features/aws-ids.yaml (+4/-0)
features/azure-ids.yaml (+4/-0)
features/cloud.py (+744/-0)
features/environment.py (+649/-80)
features/gcp-ids.yaml (+3/-0)
features/staging_commands.feature (+395/-0)
features/steps/steps.py (+332/-22)
features/ubuntu_pro.feature (+354/-0)
features/ubuntu_upgrade.feature (+71/-0)
features/unattached_commands.feature (+91/-55)
features/unattached_status.feature (+68/-14)
features/util.py (+251/-85)
help_data.yaml (+68/-0)
integration-requirements.txt (+10/-0)
lib/__init__.py (+0/-0)
lib/reboot_cmds.py (+159/-0)
lib/upgrade_lts_contract.py (+99/-0)
release-upgrades.d/ubuntu-advantage-upgrades.cfg (+4/-0)
setup.py (+25/-2)
sru/release-25/trusty-hwe-livepatch.txt (+1787/-0)
sru/release-26/xenial-enable-fips (+188/-0)
systemd/ua-auto-attach.path (+9/-0)
systemd/ua-auto-attach.service (+2/-2)
systemd/ua-reboot-cmds.service (+13/-0)
test-requirements.txt (+0/-4)
tools/base_travis_integration_tests.sh (+23/-0)
tools/constraints-bionic.txt (+1/-0)
tools/constraints-mypy.txt (+3/-0)
tools/constraints-xenial.txt (+1/-0)
tools/make-release (+120/-0)
tools/refresh-aws-pro-ids (+63/-0)
tools/refresh-keyrings.sh (+15/-22)
tools/run_aws_travis_integration_tests.sh (+22/-0)
tools/run_azure_travis_integration_tests.sh (+28/-0)
tools/run_lxd_travis_integration_tests.sh (+29/-0)
tools/tox-lxd-runner (+89/-0)
tools/ua-dev-cloud-config.yaml (+12/-0)
tox.ini (+75/-8)
uaclient/apt.py (+83/-17)
uaclient/cli.py (+391/-58)
uaclient/clouds/gcp.py (+49/-0)
uaclient/clouds/identity.py (+5/-3)
uaclient/clouds/tests/test_gcp.py (+104/-0)
uaclient/config.py (+330/-8)
uaclient/conftest.py (+5/-1)
uaclient/contract.py (+122/-47)
uaclient/defaults.py (+4/-1)
uaclient/entitlements/__init__.py (+10/-0)
uaclient/entitlements/base.py (+153/-4)
uaclient/entitlements/cc.py (+23/-11)
uaclient/entitlements/cis.py (+5/-3)
uaclient/entitlements/esm.py (+4/-3)
uaclient/entitlements/fips.py (+307/-29)
uaclient/entitlements/livepatch.py (+59/-16)
uaclient/entitlements/repo.py (+183/-65)
uaclient/entitlements/tests/conftest.py (+8/-2)
uaclient/entitlements/tests/test_base.py (+52/-0)
uaclient/entitlements/tests/test_cc.py (+8/-3)
uaclient/entitlements/tests/test_cis.py (+16/-8)
uaclient/entitlements/tests/test_esm.py (+13/-12)
uaclient/entitlements/tests/test_fips.py (+616/-131)
uaclient/entitlements/tests/test_livepatch.py (+96/-6)
uaclient/entitlements/tests/test_repo.py (+281/-41)
uaclient/exceptions.py (+32/-0)
uaclient/pip.py (+29/-0)
uaclient/status.py (+108/-11)
uaclient/tests/test_apt.py (+115/-24)
uaclient/tests/test_cli.py (+306/-52)
uaclient/tests/test_cli_attach.py (+15/-0)
uaclient/tests/test_cli_auto_attach.py (+59/-1)
uaclient/tests/test_cli_detach.py (+48/-16)
uaclient/tests/test_cli_disable.py (+146/-14)
uaclient/tests/test_cli_enable.py (+384/-9)
uaclient/tests/test_cli_refresh.py (+14/-0)
uaclient/tests/test_cli_status.py (+204/-12)
uaclient/tests/test_config.py (+462/-19)
uaclient/tests/test_contract.py (+78/-45)
uaclient/tests/test_pip.py (+91/-0)
uaclient/tests/test_reboot_cmds.py (+124/-0)
uaclient/tests/test_status.py (+3/-0)
uaclient/tests/test_upgrade_lts_contract.py (+118/-0)
uaclient/tests/test_util.py (+83/-5)
uaclient/tests/test_version.py (+11/-3)
uaclient/util.py (+76/-7)
uaclient/version.py (+16/-4)
ubuntu-advantage.1 (+3/-3)
Reviewer Review Type Date Requested Status
Bryce Harrington (community) Approve
Canonical Server Pending
Review via email: mp+398470@code.launchpad.net

Description of the change

New upstream release of 26.1 into Hirsute.

We would like to preserve rich git commit history by adding an pkg/upload/26.1 tag
pkg/ubuntu/devel(hirsute) if possible. But, not certain how we do that as part of this PR review process.

Known lintian warnings in this version we plan on addressing for our 27.0 release before trying to upload to Hirsute during feature freeze[1]:

 - lintian issues:
     - https://github.com/canonical/ubuntu-advantage-client/issues/1384

PPA package pushed and building at https://launchpad.net/~chad.smith/+archive/ubuntu/uaclient-uploads

local tests can be run w/ tox -e py3/mypy/flake8 and are also run on every PR against ubuntu-advantage-client

Daily integration tests are also run on our Jenkins[2] (VPN needs to be active)

references:
[1] FFe bug for releasing 27.0 into hirsute in a couple weeks https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bug/1915354.

[2] Jenkins UA integration tests https://jenkins.ubuntu.com/server/view/ua/

To post a comment you must log in.
Revision history for this message
Chad Smith (chad.smith) wrote :
Download full text (87.0 KiB)

----- Autopkgtest results: ---------

csmith@downtown:/tmp/ubuntu-advantage-client (ubuntu/devel)$ autopkgtest -o autopkgtest-ua-tools -U ../out/ubuntu-advantage-tools_26.1~21.04.1.dsc -- lxd ubuntu-daily:xenial
autopkgtest [14:10:51]: starting date: 2021-02-22
autopkgtest [14:10:51]: version 5.13.1
autopkgtest [14:10:51]: host downtown; command line: /usr/bin/autopkgtest -o autopkgtest-ua-tools -U '../out/ubuntu-advantage-tools_26.1~21.04.1.dsc' -- lxd ubuntu-daily:xenial
autopkgtest [14:11:01]: @@@@@@@@@@@@@@@@@@@@ test bed setup
tee: /proc/self/fd/2: Permission denied
Hit:1 http://archive.ubuntu.com/ubuntu xenial InRelease
Hit:2 http://security.ubuntu.com/ubuntu xenial-security InRelease
Hit:3 http://archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:4 http://archive.ubuntu.com/ubuntu xenial-backports InRelease
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
Calculating upgrade...
The following package was automatically installed and is no longer required:
  libfreetype6
Use 'apt autoremove' to remove it.
The following packages will be upgraded:
  libldap-2.4-2
1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 159 kB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 libldap-2.4-2 amd64 2.4.42+dfsg-2ubuntu3.13 [159 kB]
Fetched 159 kB in 0s (218 kB/s)
(Reading database ... 25810 files and directories currently installed.)
Preparing to unpack .../libldap-2.4-2_2.4.42+dfsg-2ubuntu3.13_amd64.deb ...
Unpacking libldap-2.4-2:amd64 (2.4.42+dfsg-2ubuntu3.13) over (2.4.42+dfsg-2ubuntu3.12) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up libldap-2.4-2:amd64 (2.4.42+dfsg-2ubuntu3.13) ...
Processing triggers for libc-bin (2.23-0ubuntu11.2) ...
autopkgtest [14:11:28]: testbed dpkg architecture: amd64
autopkgtest [14:11:29]: testbed running kernel: Linux 5.8.0-43-generic #49-Ubuntu SMP Fri Feb 5 03:01:28 UTC 2021
autopkgtest [14:11:29]: @@@@@@@@@@@@@@@@@@@@ source ../out/ubuntu-advantage-tools_26.1~21.04.1.dsc
Reading package lists...
Building dependency tree...
Reading state information...
Correcting dependencies...Starting pkgProblemResolver with broken count: 0
Starting 2 pkgProblemResolver with broken count: 0
Done
 Done
Starting pkgProblemResolver with broken count: 0
Starting 2 pkgProblemResolver with broken count: 0
Done
The following package was automatically installed and is no longer required:
  libfreetype6
Use 'apt autoremove' to remove it.
The following additional packages will be installed:
  binutils dpkg-dev libdpkg-perl make
Suggested packages:
  binutils-doc debian-keyring gcc | c-compiler make-doc
Recommended packages:
  gcc | c-compiler build-essential fakeroot libalgorithm-merge-perl
  libfile-fcntllock-perl
The following NEW packages will be installed:
  binutils dpkg-dev libdpkg-perl make
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
1 not fully installed or removed.
Need to get 3,242 kB of archives.
After this operation, 17.6 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu xenial-updates/ma...

Revision history for this message
Bryce Harrington (bryce) wrote :
Download full text (5.8 KiB)

### Packaging Review ###

Changelog entries look great (lots and lots of changes!)

Per the RELEASES.md documentation, the version number for hirsute should rather be just '26.1'. The ~NN.04.M suffix is only used on stable series. I've updated this manually, hopefully this is ok.

Beyond that, I just spot-checked commits to ./debian/.

In running debuild -S and debuild, I notice the packaging introduces some git delta. It's all just tmp build chaff, nothing that's actually an issue, and this is not at all unusual for packages, but if you're looking to polish the packaging might be worth tidying up:

$ debuild -S
...
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
 deleted: .pytest_cache/README.md
 deleted: .pytest_cache/v/cache/nodeids
 deleted: .pytest_cache/v/cache/stepwise

$ debuild
...
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
 modified: .pytest_cache/README.md
 modified: .pytest_cache/v/cache/nodeids

Untracked files:
  (use "git add <file>..." to include in what will be committed)
 apt-hook/hook
 apt-hook/ubuntu-advantage.pot
 debian/files
 debian/ubuntu-advantage-pro.substvars
 debian/ubuntu-advantage-pro/

Most likely just stuff to add to .gitignore. The .pytest_cache directory is in the .git repository, but maybe it shouldn't be?

### Autopkgtest ###

Passed without issue.

### Lintian ###

I'm ignoring the lintian errors since sounds like you plan to investigate and address in the future, but just for feedback these are the errors/warnings that showed for me:

E: ubuntu-advantage-tools: no-debconf-config
W: ubuntu-advantage-tools source: build-depends-on-obsolete-package Build-Depends: dh-systemd => use debhelper (>= 9.20160709)
W: ubuntu-advantage-tools: groff-message usr/share/man/man1/ubuntu-advantage.1.gz command exited with status 2: /usr/lib/man-db/zsoelim | /usr/lib/man-db/manconv -f UTF-8:ISO-8859-1 -t UTF-8//IGNORE | preconv -e UTF-8 | groff -mandoc -Z -rLL=117n -rLT=117n -wmac -Tutf8
W: ubuntu-advantage-tools: groff-message usr/share/man/man1/ubuntu-advantage.1.gz troff: Signal 11 (core dumped)
W: ubuntu-advantage-tools source: package-uses-deprecated-debhelper-compat-version 9
W: ubuntu-advantage-tools: postinst-uses-db-input
W: ubuntu-advantage-pro: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ua-auto-attach.service cloud-config.service

The man page core dumps happen to me for pretty much every package I do; I've never bothered to investigate and am guessing it's something quirky about my lxc environment.

### SRU text ###

I know this MP isn't an SRU, so am considering the SRU bug report just a draft, but I thought I'd give some feedback on it anyway.

"Version 27.0 will introduce the ability a new `ua fix <CVE_or_USN>` subcommand which will introspect the running system for a specific CVE or USN issue"

  - I think there's a small grammar issue there. Maybe drop "ability"?

"If we block the update to ubuntu-advantage-tools versio...

Read more...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
2new file mode 100644
3index 0000000..1fdfc76
4--- /dev/null
5+++ b/.github/PULL_REQUEST_TEMPLATE.md
6@@ -0,0 +1,33 @@
7+## Proposed Commit Message
8+<!-- Include a proposed commit message because all PRs can be merged in a variety of ways by the reviewer -->
9+
10+> summary: no more than 70 characters
11+>
12+> A description of what the change being made is and why it is being
13+> made, if the summary line is insufficient. The blank line above is
14+> required. This should be wrapped at 72 characters, but otherwise has
15+> no particular length requirements.
16+>
17+> If you need to write multiple paragraphs, feel free.
18+>
19+> LP: #NNNNNNN (replace with the appropriate Launchpad bug reference if applicable)
20+> Fixes: #NNNNNNN (replace with the appropriate github issue if applicable)
21+
22+## Test Steps
23+<!-- Please include any steps necessary to verify (and reproduce if
24+this is a bug fix) this change on a live deployed system,
25+including any necessary configuration files, user-data,
26+setup, and teardown. Scripts used may be attached directly to this PR. -->
27+
28+## Desired commit type::
29+<!-- put an `x` in one merge type to allow the reviewer to merge for you -->
30+ - [ ] Squash and merge: Using the "Proposed Commit Message"
31+ - [ ] Create a merge commit and use "Proposed Commit Message"
32+ - [ ] Rebase and merge, no merge commit, rely only on existing commit messages
33+
34+## Checklist:
35+<!-- Go over all the following points, and put an `x` in all the boxes
36+that apply. -->
37+ - [ ] I have updated or added any unit tests accordingly
38+ - [ ] I have updated or added any integration tests accordingly
39+ - [ ] I have updated or added any documentation accordingly
40diff --git a/.gitignore b/.gitignore
41new file mode 100644
42index 0000000..2b2da67
43--- /dev/null
44+++ b/.gitignore
45@@ -0,0 +1,24 @@
46+artifacts/*
47+*debhelper*
48+debian/ubuntu-advantage-tools/
49+debian/ubuntu-advantage-tools.substvars
50+dev/entitlement-creds.json.*
51+*.pyc
52+*.pyo
53+__pycache__
54+.tox
55+.pybuild
56+.coverage
57+.cache
58+*.egg-info/
59+.mypy_cache/
60+
61+# Ignore artifacts generated by bddeb
62+*-1~bddeb*
63+ubuntu-advantage-tools_*.orig.tar.gz
64+ubuntu-advantage-tools_all.deb
65+ubuntu_advantage_tools.dsc
66+
67+# test/jenkins artifacts
68+pytest_results.xml
69+reports/
70diff --git a/.travis.yml b/.travis.yml
71deleted file mode 100644
72index 9e571fa..0000000
73--- a/.travis.yml
74+++ /dev/null
75@@ -1,83 +0,0 @@
76-language: python
77-dist: bionic
78-
79-install:
80- # Required so `git describe` will definitely find a tag; see
81- # https://github.com/travis-ci/travis-ci/issues/7422
82- - git fetch --unshallow
83- - make testdeps
84-script:
85- - make test
86-
87-matrix:
88- fast_finish: true
89- include:
90- - python: 3.7
91- env: TOXENV=behave
92- script:
93- # bionic has lxd from deb installed, remove it first to avoid
94- # confusion over versions
95- - sudo apt-get remove --yes --purge lxd lxd-client
96- - sudo rm -Rf /var/lib/lxd
97- - sudo snap install lxd
98- - sudo lxd init --auto
99- - sudo usermod -a -G lxd $USER
100- - sg lxd -c 'make test'
101- - env:
102- PACKAGE_BUILD_SERIES=trusty
103- install:
104- - make travis-deb-install
105- script:
106- - make travis-deb-script
107- - env:
108- PACKAGE_BUILD_SERIES=xenial
109- install:
110- - make travis-deb-install
111- script:
112- - make travis-deb-script
113- - env:
114- PACKAGE_BUILD_SERIES=bionic
115- install:
116- - make travis-deb-install
117- script:
118- - make travis-deb-script
119- - env:
120- PACKAGE_BUILD_SERIES=eoan
121- install:
122- - make travis-deb-install
123- script:
124- - make travis-deb-script
125- - env:
126- PACKAGE_BUILD_SERIES=focal
127- install:
128- - make travis-deb-install
129- script:
130- - make travis-deb-script
131- - python: 3.4
132- env: TOXENV=py3-trusty,flake8-trusty
133- dist: trusty
134- - python: 3.5
135- env: TOXENV=py3-xenial,flake8-xenial
136- dist: xenial
137- - python: 3.6
138- env: TOXENV=py3-bionic,flake8-bionic
139- - python: 3.7
140- env: TOXENV=py3-eoan,flake8-eoan
141- - python: 3.8
142- env: TOXENV=py3,flake8
143- - python: 3.7
144- env: TOXENV=mypy
145- - python: 3.7
146- env: TOXENV=black
147- allow_failures:
148- - python: 3.7
149- env: TOXENV=behave
150- script:
151- # bionic has lxd from deb installed, remove it first to avoid
152- # confusion over versions
153- - sudo apt-get remove --yes --purge lxd lxd-client
154- - sudo rm -Rf /var/lib/lxd
155- - sudo snap install lxd
156- - sudo lxd init --auto
157- - sudo usermod -a -G lxd $USER
158- - sg lxd -c 'make test'
159diff --git a/Jenkinsfile b/Jenkinsfile
160new file mode 100644
161index 0000000..ebee6f9
162--- /dev/null
163+++ b/Jenkinsfile
164@@ -0,0 +1,263 @@
165+pipeline {
166+ agent any
167+
168+ environment {
169+ TMPDIR = "/tmp/$BUILD_TAG/"
170+ UACLIENT_BEHAVE_JENKINS_BUILD_TAG = "${BUILD_TAG}"
171+ UACLIENT_BEHAVE_JENKINS_CHANGE_ID = "${CHANGE_ID}"
172+ UACLIENT_BEHAVE_BUILD_PR=1
173+ UACLIENT_BEHAVE_CONTRACT_TOKEN = credentials('ua-contract-token')
174+ UACLIENT_BEHAVE_AWS_ACCESS_KEY_ID = credentials('ua-aws-access-key-id')
175+ UACLIENT_BEHAVE_AWS_SECRET_ACCESS_KEY = credentials(
176+ 'ua-aws-secret-access-key'
177+ )
178+ UACLIENT_BEHAVE_AZ_CLIENT_ID = credentials('ua-azure-client-id')
179+ UACLIENT_BEHAVE_AZ_CLIENT_SECRET = credentials(
180+ 'ua-azure-client-secret'
181+ )
182+ UACLIENT_BEHAVE_AZ_TENANT_ID = credentials('ua-azure-tenant')
183+ UACLIENT_BEHAVE_AZ_SUBSCRIPTION_ID = credentials(
184+ 'ua-azure-subscription-id'
185+ )
186+ UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING = credentials(
187+ 'ua-contract-token-staging'
188+ )
189+ JOB_SUFFIX = sh(returnStdout: true, script: "basename ${JOB_NAME}| cut -d'-' -f2").trim()
190+ }
191+
192+ stages {
193+ stage ('Setup Dependencies') {
194+ steps {
195+ deleteDir()
196+ checkout scm
197+ sh '''
198+ python3 -m venv $TMPDIR
199+ . $TMPDIR/bin/activate
200+ pip install tox # for tox supporting --parallel--safe-build
201+ '''
202+ }
203+ }
204+ stage ('Lint and Style') {
205+ parallel {
206+ stage("flake8") {
207+ steps {
208+ sh '''
209+ set +x
210+ . $TMPDIR/bin/activate
211+ tox --parallel--safe-build -e flake8
212+ '''
213+ }
214+ }
215+ stage("style") {
216+ steps {
217+ sh '''
218+ set +x
219+ . $TMPDIR/bin/activate
220+ tox --parallel--safe-build -e black
221+ '''
222+ }
223+ }
224+ stage("mypy") {
225+ steps {
226+ sh '''
227+ set +x
228+ . $TMPDIR/bin/activate
229+ tox --parallel--safe-build -e mypy
230+ '''
231+ }
232+ }
233+ }
234+ }
235+ stage ('Unit Tests') {
236+ steps {
237+ sh '''
238+ set +x
239+ . $TMPDIR/bin/activate
240+ tox --parallel--safe-build -e py3
241+ '''
242+ }
243+ }
244+ stage ('Package builds') {
245+ parallel {
246+ stage ('Package build: 14.04') {
247+ environment {
248+ BUILD_SERIES = "trusty"
249+ SERIES_VERSION = "14.04"
250+ PKG_VERSION = sh(returnStdout: true, script: "dpkg-parsechangelog --show-field Version").trim()
251+ NEW_PKG_VERSION = "${PKG_VERSION}~${SERIES_VERSION}~${JOB_SUFFIX}"
252+ ARTIFACT_DIR = "${TMPDIR}${BUILD_SERIES}"
253+ }
254+ steps {
255+ sh '''
256+ set -x
257+ mkdir ${ARTIFACT_DIR}
258+ cp debian/changelog ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
259+ sed -i "s/${PKG_VERSION}/${NEW_PKG_VERSION}/" ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
260+ dpkg-source -l${WORKSPACE}/debian/changelog-${SERIES_VERSION} -b .
261+ sbuild --nolog --verbose --dist=${BUILD_SERIES} --no-run-lintian --append-to-version=~${SERIES_VERSION} ../ubuntu-advantage-tools*${NEW_PKG_VERSION}*dsc
262+ cp ./ubuntu-advantage-tools*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-tools-${BUILD_SERIES}.deb
263+ cp ./ubuntu-advantage-pro*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-pro-${BUILD_SERIES}.deb
264+ '''
265+ }
266+ }
267+ stage ('Package build: 16.04') {
268+ environment {
269+ BUILD_SERIES = "xenial"
270+ SERIES_VERSION = "16.04"
271+ PKG_VERSION = sh(returnStdout: true, script: "dpkg-parsechangelog --show-field Version").trim()
272+ NEW_PKG_VERSION = "${PKG_VERSION}~${SERIES_VERSION}~${JOB_SUFFIX}"
273+ ARTIFACT_DIR = "${TMPDIR}${BUILD_SERIES}"
274+ }
275+ steps {
276+ sh '''
277+ set -x
278+ mkdir ${ARTIFACT_DIR}
279+ cp debian/changelog ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
280+ sed -i "s/${PKG_VERSION}/${NEW_PKG_VERSION}/" ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
281+ dpkg-source -l${WORKSPACE}/debian/changelog-${SERIES_VERSION} -b .
282+ sbuild --nolog --verbose --dist=${BUILD_SERIES} --no-run-lintian --append-to-version=~${SERIES_VERSION} ../ubuntu-advantage-tools*${NEW_PKG_VERSION}*dsc
283+ cp ./ubuntu-advantage-tools*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-tools-${BUILD_SERIES}.deb
284+ cp ./ubuntu-advantage-pro*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-pro-${BUILD_SERIES}.deb
285+ '''
286+ }
287+ }
288+ stage ('Package build: 18.04') {
289+ environment {
290+ BUILD_SERIES = "bionic"
291+ SERIES_VERSION = "18.04"
292+ PKG_VERSION = sh(returnStdout: true, script: "dpkg-parsechangelog --show-field Version").trim()
293+ NEW_PKG_VERSION = "${PKG_VERSION}~${SERIES_VERSION}~${JOB_SUFFIX}"
294+ ARTIFACT_DIR = "${TMPDIR}${BUILD_SERIES}"
295+ }
296+ steps {
297+ sh '''
298+ set -x
299+ mkdir ${ARTIFACT_DIR}
300+ cp debian/changelog ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
301+ sed -i "s/${PKG_VERSION}/${NEW_PKG_VERSION}/" ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
302+ dpkg-source -l${WORKSPACE}/debian/changelog-${SERIES_VERSION} -b .
303+ sbuild --nolog --verbose --dist=${BUILD_SERIES} --no-run-lintian --append-to-version=~${SERIES_VERSION} ../ubuntu-advantage-tools*${NEW_PKG_VERSION}*dsc
304+ cp ./ubuntu-advantage-tools*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-tools-${BUILD_SERIES}.deb
305+ cp ./ubuntu-advantage-pro*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-pro-${BUILD_SERIES}.deb
306+ '''
307+ }
308+ }
309+ stage ('Package build: 20.04') {
310+ environment {
311+ BUILD_SERIES = "focal"
312+ SERIES_VERSION = "20.04"
313+ PKG_VERSION = sh(returnStdout: true, script: "dpkg-parsechangelog --show-field Version").trim()
314+ NEW_PKG_VERSION = "${PKG_VERSION}~${SERIES_VERSION}~${JOB_SUFFIX}"
315+ ARTIFACT_DIR = "${TMPDIR}${BUILD_SERIES}"
316+ }
317+ steps {
318+ sh '''
319+ set -x
320+ mkdir ${ARTIFACT_DIR}
321+ cp debian/changelog ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
322+ sed -i "s/${PKG_VERSION}/${NEW_PKG_VERSION}/" ${WORKSPACE}/debian/changelog-${SERIES_VERSION}
323+ dpkg-source -l${WORKSPACE}/debian/changelog-${SERIES_VERSION} -b .
324+ sbuild --nolog --verbose --dist=${BUILD_SERIES} --no-run-lintian --append-to-version=~${SERIES_VERSION} ../ubuntu-advantage-tools*${NEW_PKG_VERSION}*dsc
325+ cp ./ubuntu-advantage-tools*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-tools-${BUILD_SERIES}.deb
326+ cp ./ubuntu-advantage-pro*${SERIES_VERSION}*.deb ${ARTIFACT_DIR}/ubuntu-advantage-pro-${BUILD_SERIES}.deb
327+ '''
328+ }
329+ }
330+ }
331+ }
332+ stage ('Integration Tests') {
333+ parallel {
334+ stage("lxc 14.04") {
335+ environment {
336+ UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}trusty/"
337+ UACLIENT_BEHAVE_ARTIFACT_DIR = "${TMPDIR}artifacts/behave-lxd-14.04"
338+ }
339+ steps {
340+ sh '''
341+ set +x
342+ . $TMPDIR/bin/activate
343+ tox --parallel--safe-build -e behave-lxd-14.04
344+ '''
345+ }
346+ }
347+ stage("lxc 16.04") {
348+ environment {
349+ UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}xenial/"
350+ UACLIENT_BEHAVE_ARTIFACT_DIR = "${TMPDIR}artifacts/behave-lxd-16.04"
351+ }
352+ steps {
353+ sh '''
354+ set +x
355+ . $TMPDIR/bin/activate
356+ tox --parallel--safe-build -e behave-lxd-16.04
357+ '''
358+ }
359+ }
360+ stage("lxc 18.04") {
361+ environment {
362+ UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}bionic/"
363+ UACLIENT_BEHAVE_ARTIFACT_DIR = "${TMPDIR}artifacts/behave-lxd-18.04"
364+ }
365+ steps {
366+ sh '''
367+ set +x
368+ . $TMPDIR/bin/activate
369+ tox --parallel--safe-build -e behave-lxd-18.04
370+ '''
371+ }
372+ }
373+ stage("lxc vm 20.04") {
374+ environment {
375+ UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}focal/"
376+ UACLIENT_BEHAVE_ARTIFACT_DIR = "${TMPDIR}artifacts/behave-vm-20.04"
377+ }
378+ steps {
379+ sh '''
380+ set +x
381+ . $TMPDIR/bin/activate
382+ tox --parallel--safe-build -e behave-vm-20.04
383+ '''
384+ }
385+ }
386+ stage("awspro 18.04") {
387+ environment {
388+ UACLIENT_BEHAVE_DEBS_PATH = "${TMPDIR}bionic/"
389+ UACLIENT_BEHAVE_ARTIFACT_DIR = "${TMPDIR}artifacts/behave-awspro-18.04"
390+ }
391+ steps {
392+ sh '''
393+ set +x
394+ . $TMPDIR/bin/activate
395+ tox --parallel--safe-build -e behave-awspro-18.04
396+ '''
397+ }
398+ }
399+ }
400+ }
401+ }
402+ post {
403+ always {
404+ script {
405+ try {
406+ sh '''
407+ set +x
408+ DATE=`date -d 'now+1day' +%m/%d/%Y`
409+ git clone https://github.com/canonical/server-test-scripts.git
410+ python3 server-test-scripts/ubuntu-advantage-client/lxd_cleanup.py --prefix ubuntu-behave-test-$CHANGE_ID --before-date $DATE || true
411+ '''
412+
413+ junit "pytest_results.xml"
414+ junit "reports/*.xml"
415+ } catch (Exception e) {
416+ echo e.toString()
417+ currentBuild.result = 'UNSTABLE'
418+ }
419+ try {
420+ archiveArtifacts "/tmp/${BUILD_TAG}/artifacts/**/*"
421+ } catch (Exception e) {
422+ echo "No integration test artifacts found. Presume success."
423+ }
424+ }
425+ }
426+ }
427+}
428diff --git a/Makefile b/Makefile
429index d52d1e0..e8ce957 100644
430--- a/Makefile
431+++ b/Makefile
432@@ -27,8 +27,12 @@ test:
433 @tox
434
435 testdeps:
436- pip install -U six
437+ifneq (,$(findstring trusty,$(TOXENV)))
438+ @echo Pinning virtualenv to 20.0.31 on trusty because 32 breaks py3.4
439+ pip install virtualenv==20.0.31
440+endif
441 pip install tox
442+ pip install tox-pip-version
443
444 travis-deb-install:
445 git fetch --unshallow
446@@ -48,6 +52,8 @@ travis-deb-script:
447 # Use this to get a new shell where we're in the sbuild group
448 sudo -E su ${USER} -c 'mk-sbuild ${PACKAGE_BUILD_SERIES}'
449 sudo -E su ${USER} -c 'sbuild --nolog --verbose --dist=${PACKAGE_BUILD_SERIES} ../ubuntu-advantage-tools*.dsc'
450+ cp ./ubuntu-advantage-tools*.deb ubuntu-advantage-tools-${PACKAGE_BUILD_SERIES}.deb
451+ cp ./ubuntu-advantage-pro*.deb ubuntu-advantage-tools-pro-${PACKAGE_BUILD_SERIES}.deb
452
453
454 .PHONY: build clean test testdeps demo
455diff --git a/README.md b/README.md
456index 10e88d2..c45c58b 100644
457--- a/README.md
458+++ b/README.md
459@@ -1,22 +1,122 @@
460 # Ubuntu Advantage Client
461
462-[![Build Status](https://travis-ci.org/CanonicalLtd/ubuntu-advantage-client.svg?branch=master)](https://travis-ci.org/CanonicalLtd/ubuntu-advantage-client)
463+[![Build Status](https://travis-ci.com/canonical/ubuntu-advantage-client.svg?branch=master)](https://travis-ci.com/github/canonical/ubuntu-advantage-client)
464
465 The Ubuntu Advantage client provides users with a simple mechanism to
466 view, enable, and disable offerings from Canonical on their system. The
467 following entitlements are supported:
468
469 - [Common Criteria EAL2 certification artifacts provisioning](https://ubuntu.com/cc-eal)
470-- [Canonical CIS Benchmark Audit Tool](https://ubuntu.com/cis)
471+- [Canonical CIS Benchmark Audit Tool](https://ubuntu.com/cis-audit)
472 - [Ubuntu Extended Security Maintenance](https://ubuntu.com/esm)
473 - [FIPS 140-2 Certified Modules](https://ubuntu.com/fips)
474-- [FIPS 140-2 Non-Certified Module Updates](https://ubuntu.com/fips-updates)
475+- [FIPS 140-2 Non-Certified Module Updates](https://ubuntu.com/fips)
476 - [Livepatch Service](https://www.ubuntu.com/livepatch)
477
478 ## Obtaining the Client
479
480-The client comes pre-installed on all Ubuntu systems. Users can run the
481-`ua` command to learn more or view the manpage.
482+The client comes pre-installed on all Ubuntu systems in the debian packages
483+`ubuntu-advantage-tools` package. Ubuntu Pro images on AWS and Azure Ubuntu Pro
484+images will also contain `ubuntu-advantage-pro` which automates machine attach
485+on custom AWS and Azure images.
486+
487+Users can manually run the `ua` command to learn more or view the manpage.
488+
489+## Terminology
490+ The following vocabulary is used to describe different aspects of the work
491+Ubuntu Advantage Client performs:
492+
493+| Term | Meaning |
494+| -------- | -------- |
495+| UA Client | The python command line client represented in this ubuntu-advantage-client repository. It is installed on each Ubuntu machine and is the entry-point to enable any Ubuntu Advantage commercial service on an Ubuntu machine. |
496+| Contract Server | The backend service exposing a REST API to which UA Client authenticates in order to obtain contract and commercial service information and manage which support services are active on a machine.|
497+| Entitlement/Service | An Ubuntu Advantage commercial support service such as FIPS, ESM, Livepatch, CIS-Audit to which a contract may be entitled |
498+| Affordance | Service-specific list of applicable architectures and Ubuntu series on which a service can run |
499+| Directives | Service-specific configuration values which are applied to a service when enabling that service |
500+| Obligations | Service-specific policies that must be instrumented for support of a service. Example: `enableByDefault: true` means that any attached machine **MUST** enable a service on attach |
501+
502+
503+## Architecture
504+Ubuntu Advantage client, hereafter "UA client", is python3-based command line
505+utility. It provides a CLI to attach, detach, enable,
506+disable and check status of support related services.
507+
508+The package `ubuntu-advantage-tools` also provides a C++ APT hook which helps
509+advertise ESM service and available packages in MOTD and during various apt
510+commands.
511+
512+The `ubuntu-advantage-pro` package delivers auto-attach auto-enable
513+functionality via init scripts and systemd services for various cloud
514+platforms.
515+
516+By default, Ubuntu machines are deployed in an unattached state. A machine can
517+get manually or automatically attached to a specific contract by interacting
518+with the Contract Server REST API. Any change in state of services or machine
519+attach results in additional interactions with the Contract Server API to
520+validate such operations. The contract server API is described by the
521+[ua-contracts openapi spec](https://github.com/CanonicalLtd/ua-contracts/blob/develop/docs/contracts.yaml).
522+
523+### Attaching a machine
524+Each Ubuntu SSO account holder has access to one or more contracts. To attach
525+a machine to an Ubuntu Advantage contract:
526+
527+* An Ubuntu SSO account holder must obtain a contract token from
528+https://ubuntu.com/advantage.
529+* Run `sudo ua attach <contractToken>` on the machine
530+ - Ubuntu Pro images for AWS and Azure perform an auto-attach without tokens
531+* UA Client reads config from /etc/ubuntu-advantage/uaclient.conf to obtain
532+ the contract_url (default: https://contracts.canonical.com)
533+* UA Client POSTs to the Contract Server API @
534+ <contract_url>/api/v1/context/machines/token providing the \<contractToken\>
535+* The Contract Server responds with a JSON blob containing an unique machine
536+ token, service credentials, affordances, directives and obligations to allow
537+ enabling and disabling Ubuntu Advantage services
538+* UA client writes the machine token API response to the root-readonly
539+ /var/lib/ubuntu-advantage/machine-token.json
540+* UA client auto-enables any services defined with
541+ `obligations:{enableByDefault: true}`
542+
543+### Enabling a service
544+Each service controlled by UA client will have a python module in
545+uaclient/entitlements/\*.py which handles setup and teardown of services when
546+enabled or disabled.
547+
548+If a contract entitles a machine to a service, `root` user can enable the
549+service with `ua enable <service>`. If a service can be disabled
550+`ua disabled <service>` will be permitted.
551+
552+The goal of the UA client is to remain simple and flexible and let the
553+contracts backend drive dynamic changes in contract offerings and constraints.
554+In pursuit of that goal, the UA client obtains most of it's service constraints
555+from a machine token that it obtains from the Contract Server API.
556+
557+The UA Client is simple in that it relies on the machine token on the attached
558+machine to describe whether a service is applicable for an environment and what
559+configuration is required to properly enable that service.
560+
561+Any interactions with the Contract server API are defined as UAContractClient
562+class methods in [uaclient/contract.py](uaclient/contract.py).
563+
564+## Directory layout
565+The following describes the intent of UA client related directories:
566+
567+
568+| File/Directory | Intent |
569+| -------- | -------- |
570+| ./tools | Helpful scripts used to publish, release or test various aspects of UA client |
571+| ./features/ | Behave BDD integration tests for UA Client
572+| ./uaclient/ | collection of python modules which will be packaged into ubuntu-advantage-tools package to deliver the UA Client CLI |
573+| uaclient.entitlements | Service-specific \*Entitlement class definitions which perform enable, disable, status, and entitlement operations etc. All classes derive from base.py:UAEntitlement and many derive from repo.py:RepoEntitlement |
574+| ./uaclient/cli.py | The entry-point for the command-line client
575+| ./uaclient/clouds/ | Cloud-platform detection logic used in Ubuntu Pro to determine if a given should be auto-attached to a contract |
576+| uaclient.contract | Module for interacting with the Contract Server API |
577+| ./demo | Various stale developer scripts for setting up one-off demo environments. (Not needed often)
578+| ./apt-hook/ | the C++ apt-hook delivering MOTD and apt command notifications about UA support services |
579+| ./apt-conf.d/ | apt config files delivered to /etc/apt/apt-conf.d to automatically allow unattended upgrades of ESM security-related components |
580+| /etc/ubuntu-advantage/uaclient.conf | Configuration file for the UA client.|
581+| /var/lib/ubuntu-advantage/private | `root` read-only directory containing Contract API responses, machine-tokens and service credentials |
582+| /var/log/ubuntu-advantage.log | `root` read-only log of ubuntu-advantage operations |
583+
584
585 ## Testing
586
587@@ -42,19 +142,49 @@ directory and consist of two parts: `.feature` files that define the
588 tests we want to run, and `.py` files which implement the underlying
589 logic for those tests.
590
591+By default, integration tests will do the folowing on a given cloud platform:
592+ * Launch an instance running latest daily image of the target Ubuntu release
593+ * Add the Ubuntu advantage client daily build PPA: [ppa:canonical-server/ua-client-daily](https://code.launchpad.net/~canonical-server/+archive/ubuntu/ua-client-daily)
594+ * Install the appropriate ubuntu-advantage-tools and ubuntu-advantage-pro deb
595+ * Stop the instance and snapshot it creating an updated bootable image for
596+ test runs
597+ * Launch a fresh instance based on the boot-image created and exercise tests
598+
599+The testing can be overridden to run using a local copy of the ubuntu-advantage-client source code instead of the daily PPA by providing the following environment variable to the behave test runner.
600+```UACLIENT_BEHAVE_BUILD_PR=1```
601+
602 To run the tests, you can use `tox`:
603
604 ```shell
605-tox -e behave
606+tox -e behave-20.04
607 ```
608
609 or, if you just want to run a specific file, or a test within a file:
610
611 ```shell
612-tox -e behave features/unattached_commands.feature
613-tox -e behave features/unattached_commands.feature:55
614+tox -e behave-20.04 features/unattached_commands.feature
615+tox -e behave-20.04 features/unattached_commands.feature:55
616 ```
617
618+As can be seen, this will run behave tests only for release 20.04 (Focal Fossa). We are currently
619+supporting 4 distinct releases:
620+
621+* 20.04 (Focal Fossa)
622+* 18.04 (Bionic Beaver)
623+* 16.04 (Xenial Xerus)
624+* 14.04 (Trusty Tahr)
625+
626+Therefore, to change which release to run the behave tests against, just change the release version
627+on the behave command.
628+
629+Furthermore, when developing/debugging a new scenario:
630+
631+ 1. Add a `@wip` tag decorator on the scenario
632+ 2. To only run @wip scenarios run: `tox -e behave-20.04 -- -w`
633+ 4. If you want to use a debugger:
634+ a. Add ipdb to integration-requirements.txt
635+ b. Add ipdb.set_trace() in the code block you wish to debug
636+
637 (If you're getting started with behave, we recommend at least reading
638 through [the behave
639 tutorial](https://behave.readthedocs.io/en/latest/tutorial.html) to get
640@@ -104,25 +234,138 @@ you need to add `-D reuse_container=container_name`:
641 tox -e behave -D reuse_container=container_name
642 ```
643
644+#### Integration testing on EC2
645+The following tox environments allow for testing focal on EC2:
646+
647+```
648+ # To test ubuntu-pro-images on EC2
649+ tox -e behave-awspro-20.04
650+ # To test Canonical cloud images (non-ubuntu-pro) on EC2
651+ tox -e behave-awsgeneric-20.04
652+```
653+
654+To run the test for a different release, just update the release version string. For example,
655+to run AWS pro xenial tests, you can run:
656+
657+```
658+tox -e behave-awspro-16.04
659+```
660+
661+In order to run EC2 tests the following environment variables are required:
662+ - UACLIENT_BEHAVE_AWS_ACCESS_KEY_ID
663+ - UACLIENT_BEHAVE_AWS_SECRET_ACCESS_KEY
664+
665+
666+To specifically run non-ubuntu pro tests using canonical cloud-images an
667+additional token obtained from https://ubuntu.com/advantage needs to be set:
668+ - UACLIENT_BEHAVE_CONTRACT_TOKEN=<your_token>
669+
670+By default, the public AMIs for Ubuntu Pro testing used for each Ubuntu
671+release are defined in features/aws-ids.yaml. These ami-ids are determined by
672+running `./tools/refresh-aws-pro-ids`.
673+
674+Integration tests will read features/aws-ids.yaml to determine which default
675+AMI id to use for each supported Ubuntu release.
676+
677+To update `features/aws-ids.yaml`, run `./tools/refresh-aws-pro-ids` and put up
678+a pull request against this repo to updated that content from the ua-contracts
679+marketplace definitions.
680+
681+* To manually run EC2 integration tests using packages from `ppa:canonical-server/ua-client-daily` provide the following environment vars:
682+
683+```sh
684+UACLIENT_BEHAVE_AWS_ACCESS_KEY_ID=<blah> UACLIENT_BEHAVE_AWS_SECRET_KEY=<blah2> tox -e behave-awspro-20.04
685+```
686+
687+* To manually run EC2 integration tests with a specific AMI Id provide the
688+following environment variable to launch your specfic AMI instead of building
689+a daily ubuntu-advantage-tools image.
690+```sh
691+UACLIENT_BEHAVE_REUSE_IMAGE=your-custom-ami tox -e behave-awspro-20.04
692+```
693+
694+#### Integration testing on Azure
695+The following tox environments allow for testing focal on Azure:
696+
697+```
698+ # To test ubuntu-pro-images on EC2
699+ tox -e behave-azurepro-20.04
700+ # To test Canonical cloud images (non-ubuntu-pro) on EC2
701+ tox -e behave-azuregeneric-20.04
702+```
703+
704+To run the test for a different release, just update the release version string. For example,
705+to run AWS pro xenial tests, you can run:
706+
707+```
708+tox -e behave-azurepro-16.04
709+```
710+
711+In order to run EC2 tests the following environment variables are required:
712+ - UACLIENT_BEHAVE_AZ_CLIENT_ID
713+ - UACLIENT_BEHAVE_AZ_CLIENT_SECRET
714+ - UACLIENT_BEHAVE_AZ_SUBSCRIPTION_ID
715+ - UACLIENT_BEHAVE_AZ_TENANT_ID
716+
717+
718+To specifically run non-ubuntu pro tests using canonical cloud-images an
719+additional token obtained from https://ubuntu.com/advantage needs to be set:
720+ - UACLIENT_BEHAVE_CONTRACT_TOKEN=<your_token>
721+
722+* To manually run Azure integration tests using packages from `ppa:canonical-server/ua-client-daily` provide the following environment vars:
723+
724+```sh
725+UACLIENT_BEHAVE_AZ_CLIENT_ID=<blah> UACLIENT_BEHAVE_AZ_CLIENT_SECRET=<blah2> UACLIENT_BEHAVE_AZ_SUBSCRIPTION_ID=<blah3> UACLIENT_BEHAVE_AZ_TENANT_ID=<blah4> tox -e behave-azurepro-20.04
726+```
727+
728+* To manually run Azure integration tests with a specific Image Id provide the
729+following environment variable to launch your specfic Image Id instead of building
730+a daily ubuntu-advantage-tools image.
731+```sh
732+UACLIENT_BEHAVE_REUSE_IMAGE=your-custom-image-id tox -e behave-awspro-20.04
733+```
734+
735 ## Building
736
737-The packaging for the UA client package (ubuntu-advantage-tools) is
738-in-tree, so you can build the package the way you would normally build
739-a Debian package:
740+Creating ubuntu-advantage-tools and ubuntu-advantage-pro is created from the
741+debian/control file in this repository. You can build the
742+package the way you would normally build a Debian package:
743+
744
745 ```shell
746-dpkg-buildpackage
747+dpkg-buildpackage -us -uc
748 ```
749
750-or, if you want to build for a target release other than the release
751-you're on, [configure sbuild](https://wiki.ubuntu.com/SimpleSbuild) and
752+**Note** It will build the package with dependencies for the Ubuntu release on
753+which you are building, so it's best to build in a container of kvm for the
754+release you are targeting.
755+
756+OR, if you want to build for a target release other than the release
757+you're on:
758+
759+### using sbuild
760+[configure sbuild](https://wiki.ubuntu.com/SimpleSbuild) and
761 use that for the build:
762
763+
764 ```shell
765 debuild -S
766 sbuild --dist=<target> ../ubuntu-advantage-tools_*.dsc
767 ```
768
769+### Setting up an lxc development container
770+```shell
771+lxc launch ubuntu-daily:trusty dev-t -c user.user-data="$(cat tools/ua-dev-cloud-config.yaml)"
772+lxc exec dev-t bash
773+```
774+
775+### Setting up a kvm development environment with multipass
776+**Note:** There is a sample procedure documented in tools/multipass.md as well.
777+```shell
778+multipass launch daily:focal -n dev-f --cloud-init tools/ua-dev-cloud-config.yaml
779+multipass connect dev-f
780+```
781+
782 ## Code Formatting
783
784 The `ubuntu-advantage-client` code base is formatted using
785@@ -151,15 +394,26 @@ the project, you should install them via `dev-requirements.txt`.)
786 On Launchpad, there is a [daily build recipe](https://code.launchpad.net/~canonical-server/+recipe/ua-client-daily),
787 which will build the client and place it in the [ua-client-daily PPA](https://code.launchpad.net/~canonical-server/+archive/ubuntu/ua-client-daily).
788
789-## Demo
790+## Remastering custom golden images based on Ubuntu PRO
791
792-Users can demo the client with a local backend. This can be done with
793-the following:
794+Vendors who wish to provide custom images based on Ubuntu PRO images can
795+follow the procedure below:
796
797-```shell
798-# Set up ua-contracts in a docker container in a bionic lxc on port 3000
799-make demo
800-# Set up two clients pointing at the local contract server
801-./demo/run-uaclient --series disco
802-./demo/run-uaclient --series xenial -b multipass
803+* Launch the Ubuntu PRO golden image
804+* Customize your golden image as you see fit
805+* If `ua status` shows attached, remove the UA artifacts to allow clean
806+ auto-attach on subsequent cloned VM launches
807+```bash
808+sudo ua detach
809+sudo rm -rf /var/log/ubuntu-advantage.log # to remove credentials and tokens from logs
810 ```
811+* Remove `cloud-init` first boot artifacts so the cloned VM boot is seen as a first boot
812+```bash
813+sudo cloud-init clean --logs
814+sudo shutdown -h now
815+```
816+* Use your cloud platform to clone or snapshot this VM as a golden image
817+
818+
819+## Releasing ubuntu-adantage-tools
820+see [RELEASES.md](RELEASES.md)
821diff --git a/RELEASES.md b/RELEASES.md
822new file mode 100644
823index 0000000..e31dc37
824--- /dev/null
825+++ b/RELEASES.md
826@@ -0,0 +1,63 @@
827+# Ubuntu Advantage Client Releases
828+
829+## Release versioning schemes:
830+
831+Below are the versioning schemes used for publishing debs:
832+
833+| Build target | Version Format |
834+| -------- | -------- |
835+| Devel series upstream release | XX.YY |
836+| Devel series bugfix release | XX.YY.Z~ubuntu1|
837+| Stable series release | XX.YY~ubuntu1~18.04.1|
838+| [Daily Build Recipe](https://code.launchpad.net/~canonical-server/+recipe/ua-client-daily) | XX.YY+<revtime>-g<commitish>~ubuntu1~18.04.1 |
839+| Ubuntu PRO series release | binary copies of Daily PPA |
840+
841+## Supported upgrade use-cases based on version formats
842+
843+| Upgrade path | Version diffs |
844+| LTS to LTS | 20.3~ubuntu1~14.04.1 -> 20.3~ubuntu1~16.04.1 |
845+| LTS to Daily PPA | 20.3~ubuntu1~14.04.1 -> 20.3+202004011202~ubuntu1~14.04.1 |
846+| Ubuntu PRO to latest <series>-updates | 20.3+202004011202~ubuntu1~14.04.1 -> 20.4~ubuntu1~14.04.1 |
847+| Ubuntu PRO to Daily PPA | 20.3+202004011202~ubuntu1~14.04.1 -> 20.4+202004021202~ubuntu1~14.04.1 |
848+
849+
850+## Devel Release Process
851+
852+Below is the procedure used to release ubuntu-advantage-client to an Ubuntu series:
853+
854+ 1. git-ubuntu clone ubuntu-advantage-tools; cd ubuntu-advantage-tools
855+ 2. git remote add upstream git@github.com:canonical/ubuntu-advantage-client.git
856+ 3. git fetch upstream
857+ 4. git checkout upstream/release-24
858+ 5. git tag -a 24.3
859+ 6. git rebase --onto pkg/ubuntu/devel 24.2 24.3
860+ 7. git checkout -b pkg-upload-24.3
861+ 8. debuild -S
862+ 9. dput ppa:chad.smith/ua-client-uploads ./ubuntu-advantage-tools_24.3_source.changes
863+ 10. create a release tarfile/changes file using uss-tableflip's build-package script
864+```
865+ ./tools/make-release --series <SERIES> --ppa <PPA_URL_FOR_TEST_UPLOADS>
866+ # follow printed procedure for dput to test PPA, and tag the released commitish
867+```
868+ 11. Create a merge proposal such as [24.2 PR](https://code.launchpad.net/~chad.smith/ubuntu/+source/ubuntu-advantage-tools/+git/ubuntu-advantage-tools/+merge/385073), [24.3 PR](https://code.launchpad.net/~chad.smith/ubuntu/+source/ubuntu-advantage-tools/+git/ubuntu-advantage-tools/+merge/389745)
869+ 11. For SRUs: Create an "upload a new version bug" in https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bugs
870+ 12. Describe the need, provide testing PPA and describe test instructions
871+ 13. Ping appropriate maintainer about upload and verification
872+ 14. Validate tests, accept upload and ship it
873+
874+Previous release bugs:
875+| ------- |
876+| [20.3 Focal](https://bugs.launchpad.net/ubuntu/+source/ubuntu-advantage-tools/+bug/1869980) |
877+
878+
879+## Ubuntu PRO Release Process
880+
881+Manually perform a binary package copy from Daily PPA to Premium PPA and notify image creators
882+
883+ 1. [Open Daily PPA copy-package operation](https://code.launchpad.net/~canonical-server/+archive/ubuntu/ua-client-daily/+copy-packages)
884+ 2. Check Trusty, Xenial, Bionic package
885+ 3. Select Destination PPA: UA Client Premium [~canonical-server/ubuntu/ua-client-premium]
886+ 4. Select Destination series: The same series
887+ 5. Copy options: "Copy existing binaries
888+ 6. Click Copy packages
889+ 7. Notify Pro Image creatros about expected Premium PPA version (patviafore/rcj)
890diff --git a/apt-hook/hook.cc b/apt-hook/hook.cc
891index 56032f4..ed009de 100644
892--- a/apt-hook/hook.cc
893+++ b/apt-hook/hook.cc
894@@ -33,8 +33,10 @@
895 #include <locale.h>
896
897 struct result {
898- int enabled_esms;
899- int disabled_esms;
900+ int enabled_esms_i;
901+ int disabled_esms_i;
902+ int enabled_esms_a;
903+ int disabled_esms_a;
904 };
905
906 // Return parent pid of specified pid, using /proc (pid might be self)
907@@ -122,10 +124,22 @@ static void check_esm_upgrade(pkgCache::PkgIterator pkg, pkgPolicy *policy, resu
908 // TODO: Just look at the origin, not pinning.
909 if (pf.File().Archive() != 0 && pf.File().Origin() == std::string("UbuntuESM"))
910 {
911+ // Xenial and later should not be advertising unauthenticated ESM Infra apt repos
912 if (policy->GetPriority(pf.File()) == -32768)
913- res.disabled_esms++;
914+ res.disabled_esms_i++;
915 else
916- res.enabled_esms++;
917+ res.enabled_esms_i++;
918+
919+ return;
920+ }
921+
922+ if (pf.File().Archive() != 0 && pf.File().Origin() == std::string("UbuntuESMApps"))
923+ {
924+ // Xenial and later should not be advertising unauthenticated ESM Apps apt repos
925+ if (policy->GetPriority(pf.File()) == -32768)
926+ res.disabled_esms_a++;
927+ else
928+ res.enabled_esms_a++;
929
930 return;
931 }
932@@ -187,15 +201,16 @@ int main(int argc, char *argv[])
933 // assert(ppid == getppid_of("self"));
934 // }
935 assert(cmdline_eligible(make_cmdline("apt\0update\0")));
936- assert(cmdline_eligible(make_cmdline("apt-get\0update\0")));
937- assert(!cmdline_eligible(make_cmdline("apt-get\0install\0")));
938- assert(!cmdline_eligible(make_cmdline("apt\0install\0")));
939 assert(!cmdline_eligible(make_cmdline("apt\0install\0")));
940 assert(cmdline_eligible(make_cmdline("aptitude\0upgrade\0")));
941 assert(cmdline_eligible(make_cmdline("aptitude\0update\0")));
942 command_used = "";
943
944- result res = {0, 0};
945+ result res = {0, 0, 0, 0};
946+
947+ // useful for testing
948+ if (has_arg(argv, "test"))
949+ command_used = "update";
950
951 if (has_arg(argv, "test") || cmdline_eligible(getcmdline(getppid_of(getppid_of("self")))))
952 get_update_count(res);
953@@ -205,27 +220,51 @@ int main(int argc, char *argv[])
954 return 1;
955 }
956
957- if (res.enabled_esms > 0 && (command_used == "update"))
958+ if (command_used == "update")
959 {
960- ioprintf(std::cout,
961- ngettext("%d of the updates is from UA Infrastructure ESM.",
962- "%d of the updates are from UA Infrastructure ESM.",
963- res.enabled_esms),
964- res.enabled_esms);
965- ioprintf(std::cout, "\n");
966+ if (res.enabled_esms_i > 0)
967+ {
968+ ioprintf(std::cout,
969+ ngettext("%d of the updates is from UA Infra: ESM.",
970+ "%d of the updates are from UA Infra: ESM.",
971+ res.enabled_esms_i),
972+ res.enabled_esms_i);
973+ ioprintf(std::cout, "\n");
974+ }
975+ if (res.enabled_esms_a > 0)
976+ {
977+ ioprintf(std::cout,
978+ ngettext("%d of the updates is from UA Apps: ESM.",
979+ "%d of the updates are from UA Apps: ESM.",
980+ res.enabled_esms_a),
981+ res.enabled_esms_a);
982+ ioprintf(std::cout, "\n");
983+ }
984 }
985
986- if (res.disabled_esms > 0)
987+ if (res.disabled_esms_i > 0 || res.disabled_esms_a > 0)
988 {
989 if (command_used != "update")
990 std::cout << std::endl;
991- ioprintf(std::cout,
992- ngettext("%d additional update is available with UA Infrastructure ESM.",
993- "%d additional updates are available with UA Infrastructure ESM.",
994- res.disabled_esms),
995- res.disabled_esms);
996+ if (res.disabled_esms_i > 0)
997+ {
998+ ioprintf(std::cout,
999+ ngettext("%d additional update is available with UA Infra: ESM.",
1000+ "%d additional updates are available with UA Infra: ESM.",
1001+ res.disabled_esms_i),
1002+ res.disabled_esms_i);
1003+ ioprintf(std::cout, "\n");
1004+ }
1005+ if (res.disabled_esms_a > 0)
1006+ {
1007+ ioprintf(std::cout,
1008+ ngettext("%d additional update is available with UA Apps: ESM.",
1009+ "%d additional updates are available with UA Apps ESM.",
1010+ res.disabled_esms_a),
1011+ res.disabled_esms_a);
1012+ ioprintf(std::cout, "\n");
1013+ }
1014
1015- ioprintf(std::cout, "\n");
1016 ioprintf(std::cout, gettext("To see these additional updates run: apt list --upgradable"));
1017 ioprintf(std::cout, "\n");
1018 ioprintf(std::cout, gettext("See https://ubuntu.com/advantage or run: sudo ua status"));
1019diff --git a/debian/changelog b/debian/changelog
1020index 001d980..1180c0f 100644
1021--- a/debian/changelog
1022+++ b/debian/changelog
1023@@ -1,3 +1,228 @@
1024+ubuntu-advantage-tools (26.1~21.04.1) hirsute; urgency=medium
1025+
1026+ * New upstream release 26.1
1027+ - contract: block detach call to contract if machine-id change
1028+ - docs: add readme docs about mastering clean golden images
1029+ - fips: add reboot notices for fips operations (GH: #1368)
1030+ - livepatch: add retry when running canonical-livepatch status
1031+ (GH: #1360)
1032+ - util: use lru_cache to avoid re-reading os-release and machine-id
1033+ (GH: #1329)
1034+ - tests:
1035+ + add disable_auto_attach config to all test PRO vms
1036+ + add more log artifacts during failed integration test
1037+ + check cloudinit status after launching image
1038+ + mock leaking livepatch.application_status for fips test
1039+ + retry package installs on apt exit 100
1040+ - jenkins: parameterize build stages to avoid parallel job collision
1041+
1042+ -- Lucas Moura <lucas.moura@canonical.com> Fri, 19 Feb 2021 10:30:22 -0300
1043+
1044+ubuntu-advantage-tools (26.0.1~21.04.1) hirsute; urgency=medium
1045+
1046+ * auto-attach: fix comparing numeric iid
1047+
1048+ -- Lucas Moura <lucas.moura@canonical.com> Fri, 05 Feb 2021 14:10:09 -0300
1049+
1050+ubuntu-advantage-tools (26.0~21.04.1) hirsute; urgency=medium
1051+
1052+ * New upstream release 26.0:
1053+ - auto-attach: systemd unit to run before ua-reboot-cmds.service
1054+ - config: remove_notice should remove notices.json when empty
1055+ - fips:
1056+ + add notice if running a deactivated FIPS kernel (GH: #1348)
1057+ + block enabling FIPS on clouds using Xenial
1058+ + block enabling fips on GCP instances
1059+ + check /proc/sys/crypto/fips_enable to see if fips is enabled
1060+ + override fips metapackage when on bionic cloud
1061+ + update metapackage override logic on fips
1062+ - notices: clear lock file and notice when encountering any exception
1063+ (GH: #1326)
1064+ - reboot_cmds: retry on lock held errors due to pro auto-attach
1065+ - services: allow uaclient to disable services during enable
1066+ - status: include beta services in json formatted output with --all
1067+ (GH: #1341)
1068+ - tests:
1069+ + add FIPS tests to AWS and Azure bionic images
1070+ + add GCP pro test for focal machine
1071+ + add after_step collection of artifacts on failure
1072+ + remove proc file check after disabling fips
1073+ + pro: block auto-attach with cloud-config bootcmd
1074+ + add validation of systemd unit ua-reboot-cmds.service
1075+ + test enabling fips-updates when fips is enabled
1076+ - jenkins:
1077+ - add deb build stage to assert package builds
1078+ - use series-specific sbuild --build-dir avoid races
1079+ - use --append-to-version for each sbuild run to avoid races
1080+ - presume success when no integration artifacts created
1081+
1082+ -- Lucas Moura <lucas.moura@canonical.com> Thu, 04 Feb 2021 16:34:56 -0300
1083+
1084+ubuntu-advantage-tools (26.0~21.04.1~beta) hirsute; urgency=medium
1085+
1086+ * d/rules:
1087+ - add --with systemd to allow reboot init script
1088+ - do not remove lib/systemd/system folder
1089+ * d/postinst:
1090+ - create marker file when reboot script need to run:
1091+ - enable livepatch across trusty to xenial upgrade
1092+ - update fips on existing fips pro machines
1093+ * New upstream release 26.0~beta:
1094+ - gcp: add Google Cloud Platform support (GH #1269)
1095+ - fips:
1096+ + remove is_beta from fips sevices
1097+ + fips pro: add upgrade support to require reboot to unmark held fips pkgs
1098+ + update origin UbuntuFIPSUpdates
1099+ - status:
1100+ + add notice to tabular output
1101+ + held locks emit notice about Operation in progress
1102+ - cli: help sort output so trusty ordering matches xenial++
1103+ - cis: rename service from cis-audit
1104+ - config: provide config notices and add_notice and remove_notice methods
1105+ - contract: add resource-machine-access route and datapath
1106+ - init: add init script to run commands on reboot
1107+ - keys: add ubuntu-advantage-cis keyring
1108+ - livepatch: make livepatch react to enableByDefault delta
1109+ - log: log when we install pkgs because of contract delta
1110+ - make: drop six testdeps target
1111+ - pro: do not install pro debs on non-pro instances
1112+ - services: Update beta info for services (GH #1220)
1113+ - tools: add tox-lxd-runner, that execute the test command in a shell
1114+ - tools: refresh-keyrings handles cis keys. drop series-specific keys
1115+ - tests:
1116+ + add GCE support for integration tests
1117+ + add cis integration tests for unattached and pro
1118+ + add pytest constraint for mypy tests
1119+ + add unittests for reboot_cmds script
1120+ + fix esm package messages for new update notifier version
1121+ + pin importlib-metadata for mypy tests
1122+ + repo tests for request_resource_machine_access
1123+ + unit tests for config cache clearing and machine-access data
1124+ - jenkins:
1125+ + add basic Jenkinsfile for CI runs per PR
1126+ + add jenkins parseable test results
1127+ + add lxc cleanup stage on Jenkinsfile
1128+
1129+ -- Lucas Moura <lucas.moura@canonical.com> Thu, 14 Jan 2021 10:08:20 -0300
1130+
1131+ubuntu-advantage-tools (25.0~20.10.1) groovy; urgency=medium
1132+
1133+ * Release version 25.0
1134+
1135+ -- Chad Smith <chad.smith@canonical.com> Fri, 04 Dec 2020 13:32:16 -0700
1136+
1137+ubuntu-advantage-tools (25.0~20.10.1beta3) groovy; urgency=medium
1138+
1139+ * New upstream release 25.0~beta3:
1140+ - upgrade-lts-conract: noop during do-release-upgrade on unattached
1141+ (GH: #1255)
1142+ - ua-auto-attach: order systemd unit before cloud-config.service
1143+ - Update FIPSUpdates pin origin
1144+ - fips: unmark held fips packages for ubuntu pro fips image support
1145+ (GH: #1109)
1146+ - repo: handle changes to additionalPackages contract deltas
1147+ - repo: move package installation to install_packages method
1148+ - pro: trigger auto-attach as soon as instance-data.json is available
1149+ (GH: #1234)
1150+ - Conditionally install packages when enabling FIPS
1151+ - fips: allow disable (GH: #1168)
1152+ - cli: add trailing newline to argparse errors (GH: #1236)
1153+ - Install fips metapacking when enabling service
1154+ - integration test improvements:
1155+ + upgrade-test: fix upgrade path restart failures on trusty (GH: #1257)
1156+ + Fix integration test setup scripts (GH: #1253)
1157+ + strict checking for command success on behave
1158+ + Update tests to use new pycloudlib LXD abstraction
1159+ + Add upgrade scenario tests when FIPS is enabled
1160+ + Improve FIPS tests for checking packages
1161+ + Update esm-infra xenial lxd test
1162+ + Fix vm tests as esm-apps is beta service
1163+ + Fix azure generic integration testing
1164+ + Update esm-apps check on staging_commands tests
1165+ + Install pycloudlib for azure jobs only
1166+ + Fix shell condition in run_azure_travis_integration_tests.sh
1167+ + Update azure jobs on travis
1168+ + Update travis url in README
1169+ + Update travis scripts to use ppa only on master
1170+ + Fix cron event type check on travis yaml
1171+
1172+ -- Chad Smith <chad.smith@canonical.com> Wed, 02 Dec 2020 13:43:16 -0700
1173+
1174+ubuntu-advantage-tools (25.0~20.10.1~beta2) groovy; urgency=medium
1175+
1176+ * New upstream release 25.0~beta2:
1177+ - help: update esm-infra help text (GH: #1212)
1178+ - apt-hook: update apt cli messaging for UA Infra: ESM and UA Apps: ESM
1179+ product names
1180+ - help: update fips help docs (GH: #1213)
1181+ - help: revert CIS help doc URL (GH: #1211)
1182+ - help: add new fips help URLs to CLI help docs (GH: #1210)
1183+ - Show error when enabling service with invalid repo [Lucas Moura]
1184+ (GH: #954)
1185+ - Update beta info for services (#1220) [Lucas Moura] (GH: #1216)
1186+ - Do not enable fips when fips-updates is active [Lucas Moura] (GH: #1209)
1187+ - Add vm test commands in tox.ini (#1204) [Lucas Moura]
1188+
1189+ -- Chad Smith <chad.smith@canonical.com> Mon, 26 Oct 2020 20:01:21 -0600
1190+
1191+ubuntu-advantage-tools (25.0~20.10.1~beta1) groovy; urgency=medium
1192+
1193+ * Beta bug fix release
1194+ - status: fix missing description_override key after upgrade from
1195+ trusty (GH: #1201)
1196+ - During contract delta processing use _check_application_status_on_cache
1197+ instead of live service status
1198+
1199+ -- Chad Smith <chad.smith@canonical.com> Sat, 10 Oct 2020 21:47:21 -0600
1200+
1201+ubuntu-advantage-tools (25.0~20.10.1~beta) groovy; urgency=medium
1202+
1203+ * d/control:
1204+ - add po-debconf dependency and fix lintian not-using-po-debconf and
1205+ untranslatable-debconf-templates
1206+ - add ${misc:Depends} dep to ubuntu-advantage-pro to fix lintian
1207+ debhelper-but-no-misc-depends (GH: #1024)
1208+ * d/rules:
1209+ - drop --with systemd fix build-depends-on-obsolete-package
1210+ - set fix lintian warning extra:Depends even if empty
1211+ * d/postrm
1212+ - Add more gpg keys to be deleted in postrm for Xenial+ support
1213+ * d/postinst:
1214+ - do not unconfigure non-trusty esm. no series in apt filenames (GH: #1170)
1215+ - check if esm is already enabled (GH: #1095)
1216+ * New upstream release 25.0:
1217+ - Do not uninstall additionalPackages or livepatch when disabling services
1218+ - check for issubclass on clean_apt_files
1219+ - Add do-release-upgrade support for esm-infra and apps suites (GH: #1169)
1220+ - Apply contract deltas during do-release-upgrade operations
1221+ - cli: add ua help command
1222+ - cli: status add blocking --wait param and lock files for config change
1223+ - Fix livepatch behaviour on aws pro focal machine
1224+ - travis: drop inapplicable workspaces from specific awsgeneric release
1225+ jobs
1226+ - Add possible reboot text after enabling/disabling services
1227+ - apt-hook: package apt-hook and apt configuration files on all releases
1228+ (GH: #1150)
1229+ - Fix enable fail bug
1230+ - Add uaclient.conf override mechanism for auto-attach, beta services and
1231+ machine-token
1232+ - Support ESM Apps [Brian Murray] (GH: #930)
1233+ - Do not enable services if blocking services is active (GH: #1029)
1234+ - contract: handle 401 on invalid token, 403 on expired (GH: #1335)
1235+ - Hide beta services from default status output and enable/disable
1236+ operations (GH: #1079) (GH: #1091)
1237+ - fips: force apt noninteractive prompts during package installs
1238+ (GH: #1084)
1239+ - tests: add unit tests for aws-gov/aws-china cloud detection
1240+ - Add AWS China and GovCloud partitions [Robert Jennings]
1241+ - Disable beta services to be show/enabled without flag
1242+ - Add missing build_pr command to environment
1243+ - Use additionalPackages from service payload
1244+ - Add integration testing for Travis runs [patriciadomin] (GH: #856)
1245+ (GH: #857) (GH: #853)
1246+
1247+ -- Chad Smith <chad.smith@canonical.com> Mon, 28 Sep 2020 21:11:54 -0600
1248+
1249 ubuntu-advantage-tools (24.4) groovy; urgency=medium
1250
1251 * New bug-fix-only release 24.4:
1252diff --git a/debian/control b/debian/control
1253index 0fedb9c..9c3095e 100644
1254--- a/debian/control
1255+++ b/debian/control
1256@@ -5,16 +5,17 @@ Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
1257 Build-Depends: bash-completion,
1258 debhelper (>=9),
1259 dh-python,
1260+ dh-systemd,
1261 gettext,
1262 git,
1263 libapt-pkg-dev,
1264+ po-debconf,
1265 python3 (>= 3.4),
1266 python3-flake8,
1267 python3-mock,
1268 python3-pytest,
1269 python3-setuptools,
1270- python3-yaml,
1271- dh-systemd
1272+ python3-yaml
1273 Standards-Version: 4.3.0
1274 Homepage: https://buy.ubuntu.com
1275 Vcs-Git: https://github.com/CanonicalLtd/ubuntu-advantage-script.git
1276@@ -37,7 +38,7 @@ Description: management tools for Ubuntu Advantage
1277
1278 Package: ubuntu-advantage-pro
1279 Architecture: any
1280-Depends: ubuntu-advantage-tools (>=20.2)
1281+Depends: ${misc:Depends}, ubuntu-advantage-tools (>=20.2)
1282 Replaces: ubuntu-advantage-tools (<<20.2)
1283 Breaks: ubuntu-advantage-tools (<<20.2)
1284 Description: utilities and services for Ubuntu Pro images
1285diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in
1286new file mode 100644
1287index 0000000..986f7cc
1288--- /dev/null
1289+++ b/debian/po/POTFILES.in
1290@@ -0,0 +1 @@
1291+[type: gettext/rfc822deb] ubuntu-advantage-tools.templates
1292diff --git a/debian/po/templates.pot b/debian/po/templates.pot
1293new file mode 100644
1294index 0000000..9dd5145
1295--- /dev/null
1296+++ b/debian/po/templates.pot
1297@@ -0,0 +1,36 @@
1298+# SOME DESCRIPTIVE TITLE.
1299+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
1300+# This file is distributed under the same license as the ubuntu-advantage-tools package.
1301+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
1302+#
1303+#, fuzzy
1304+msgid ""
1305+msgstr ""
1306+"Project-Id-Version: ubuntu-advantage-tools\n"
1307+"Report-Msgid-Bugs-To: ubuntu-advantage-tools@packages.debian.org\n"
1308+"POT-Creation-Date: 2020-10-02 15:07-0600\n"
1309+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1310+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1311+"Language-Team: LANGUAGE <LL@li.org>\n"
1312+"Language: \n"
1313+"MIME-Version: 1.0\n"
1314+"Content-Type: text/plain; charset=CHARSET\n"
1315+"Content-Transfer-Encoding: 8bit\n"
1316+
1317+#. Type: note
1318+#. Description
1319+#: ../ubuntu-advantage-tools.templates:1001
1320+msgid "Ubuntu Pro support now requires ubuntu-advantage-pro"
1321+msgstr ""
1322+
1323+#. Type: note
1324+#. Description
1325+#: ../ubuntu-advantage-tools.templates:1001
1326+msgid "To install run the following:"
1327+msgstr ""
1328+
1329+#. Type: note
1330+#. Description
1331+#: ../ubuntu-advantage-tools.templates:1001
1332+msgid "sudo apt-get install ubuntu-advantage-pro"
1333+msgstr ""
1334diff --git a/debian/postinst b/debian/postinst
1335index 3b39c9d..5d942f9 100644
1336--- a/debian/postinst
1337+++ b/debian/postinst
1338@@ -11,12 +11,15 @@ UA_KEYRING_DIR="/usr/share/keyrings/"
1339 ESM_INFRA_KEY_TRUSTY="ubuntu-advantage-esm-infra-trusty.gpg"
1340
1341 APT_SRC_DIR="/etc/apt/sources.list.d"
1342+APT_PREFERENCES_DIR="/etc/apt/preferences.d"
1343 ESM_APT_SOURCE_FILE_PRECISE="$APT_SRC_DIR/ubuntu-esm-precise.list"
1344 ESM_APT_SOURCE_FILE_TRUSTY="$APT_SRC_DIR/ubuntu-esm-trusty.list"
1345-ESM_INFRA_APT_SOURCE_FILE_TRUSTY="$APT_SRC_DIR/ubuntu-esm-infra-trusty.list"
1346+ESM_INFRA_OLD_APT_SOURCE_FILE_TRUSTY="$APT_SRC_DIR/ubuntu-esm-infra-trusty.list"
1347+ESM_INFRA_APT_SOURCE_FILE_TRUSTY="$APT_SRC_DIR/ubuntu-esm-infra.list"
1348
1349 ESM_APT_PREF_FILE_TRUSTY="/etc/apt/preferences.d/ubuntu-esm-trusty"
1350-ESM_INFRA_APT_PREF_FILE_TRUSTY="/etc/apt/preferences.d/ubuntu-esm-infra-trusty"
1351+ESM_INFRA_OLD_APT_PREF_FILE_TRUSTY="/etc/apt/preferences.d/ubuntu-esm-infra-trusty"
1352+ESM_INFRA_APT_PREF_FILE_TRUSTY="/etc/apt/preferences.d/ubuntu-esm-infra"
1353
1354 MYARCH="$(dpkg --print-architecture)"
1355 ESM_SUPPORTED_ARCHS="i386 amd64"
1356@@ -25,14 +28,80 @@ SYSTEMD_WANTS_AUTO_ATTACH_LINK="/etc/systemd/system/multi-user.target.wants/ua-a
1357 SYSTEMD_HELPER_ENABLED_AUTO_ATTACH_DSH="/var/lib/systemd/deb-systemd-helper-enabled/ua-auto-attach.service.dsh-also"
1358 SYSTEMD_HELPER_ENABLED_WANTS_LINK="/var/lib/systemd/deb-systemd-helper-enabled/multi-user.target.wants/ua-auto-attach.service"
1359
1360+REBOOT_CMD_MARKER_FILE="/var/lib/ubuntu-advantage/marker-reboot-cmds-required"
1361+
1362+# Rename apt config files for ua services removing ubuntu release names
1363+redact_ubuntu_release_from_ua_apt_filenames() {
1364+ DIR=$1
1365+ UA_SERVICES=$(python3 -c "
1366+from uaclient.entitlements import ENTITLEMENT_CLASS_BY_NAME
1367+print(*ENTITLEMENT_CLASS_BY_NAME.keys(), sep=' ')
1368+")
1369+
1370+ for file in "$DIR"/*; do
1371+ release_name=""
1372+ case "$file" in
1373+ *-trusty*)
1374+ release_name=trusty;;
1375+ *-xenial*)
1376+ release_name=xenial;;
1377+ *-bionic*)
1378+ release_name=bionic;;
1379+ *-focal*)
1380+ release_name=focal;;
1381+ *-groovy*)
1382+ release_name=groovy;;
1383+ *) release_name="";;
1384+ esac
1385+ if [ "$release_name" ]; then
1386+ # We have a ubuntu release name in the apt config.
1387+ # Remove $release_name from original $file.
1388+ new_file=${file%-${release_name}*}${file#*${release_name}}
1389+ for service in ${UA_SERVICES}; do
1390+ if [ "${file#*$service}" != "$file" ]; then
1391+ # Valid apt cfg file for an ubuntu-advantage service
1392+ mv "$file" "$new_file"
1393+ fi
1394+ done
1395+ fi
1396+ done
1397+}
1398+
1399+
1400+# Check cached service status from status.json and return 0 if enabled else 1
1401+check_service_is_enabled() {
1402+ service_name=$1
1403+ _RET=$(python3 -c "
1404+import os
1405+import json
1406+from uaclient.config import UAConfig
1407+cfg = UAConfig()
1408+status = cfg.read_cache('status-cache')
1409+if status:
1410+ for service in status['services']:
1411+ if service['name'] == '${service_name}':
1412+ print(service['status'])
1413+")
1414+ if [ "${_RET}" = "enabled" ]; then
1415+ return 0
1416+ else
1417+ return 1
1418+ fi
1419+}
1420+
1421
1422 unconfigure_esm() {
1423- rm -f $APT_TRUSTED_KEY_DIR/ubuntu-esm*gpg # Remove previous esm keys
1424- rm -f $APT_TRUSTED_KEY_DIR/$ESM_INFRA_KEY_TRUSTY
1425- rm -f $ESM_INFRA_APT_SOURCE_FILE_TRUSTY
1426- rm -f $ESM_APT_PREF_FILE_TRUSTY $ESM_INFRA_APT_PREF_FILE_TRUSTY
1427+ if ! check_service_is_enabled esm-infra; then
1428+ rm -f $APT_TRUSTED_KEY_DIR/ubuntu-esm*gpg # Remove previous esm keys
1429+ rm -f $APT_TRUSTED_KEY_DIR/$ESM_INFRA_KEY_TRUSTY
1430+ rm -f $ESM_INFRA_APT_SOURCE_FILE_TRUSTY
1431+ rm -f $ESM_INFRA_OLD_APT_SOURCE_FILE_TRUSTY
1432+ rm -f $ESM_APT_PREF_FILE_TRUSTY $ESM_INFRA_OLD_APT_PREF_FILE_TRUSTY
1433+ rm -f $ESM_INFRA_APT_PREF_FILE_TRUSTY
1434+ fi
1435 }
1436
1437+
1438 configure_esm() {
1439 rm -f $APT_TRUSTED_KEY_DIR/ubuntu-esm*gpg # Remove previous esm keys
1440 if [ ! -f "$APT_TRUSTED_KEY_DIR/$ESM_INFRA_KEY_TRUSTY" ]; then
1441@@ -64,6 +133,29 @@ EOF
1442 fi
1443 }
1444
1445+
1446+# If held fips packages exist, we are on a FIPS PRO machine with FIPS enabled
1447+mark_reboot_for_fips_pro() {
1448+ FIPS_HOLDS=$(apt-mark showholds | grep -E 'fips|libssl1|openssh-client|openssh-server|linux-fips|openssl|strongswan' || exit 0)
1449+ if [ "$FIPS_HOLDS" ]; then
1450+ mark_reboot_cmds_as_needed MESSAGE_FIPS_REBOOT_REQUIRED
1451+ fi
1452+}
1453+
1454+
1455+mark_reboot_cmds_as_needed() {
1456+ msg_name=$1
1457+ if [ ! -f "$REBOOT_CMD_MARKER_FILE" ]; then
1458+ touch $REBOOT_CMD_MARKER_FILE
1459+ fi
1460+ python3 -c "
1461+from uaclient.config import UAConfig
1462+from uaclient.status import ${msg_name}
1463+cfg = UAConfig()
1464+cfg.add_notice(label='', description=${msg_name})
1465+"
1466+}
1467+
1468 case "$1" in
1469 configure)
1470 PREVIOUS_PKG_VER=$2
1471@@ -94,10 +186,15 @@ case "$1" in
1472 # ubuntu-advantage-pro package that should be installed
1473 . /usr/share/debconf/confmodule
1474 db_input high ubuntu-advantage-tools/suggest_pro_pkg || true
1475- db_go
1476+ db_go || true
1477 fi
1478 fi
1479
1480+ # UA service PPAs support all ubuntu releases, no need to
1481+ # specialize apt config filenames per ubuntu release.
1482+ redact_ubuntu_release_from_ua_apt_filenames $APT_SRC_DIR
1483+ redact_ubuntu_release_from_ua_apt_filenames $APT_PREFERENCES_DIR
1484+
1485 # CACHE_DIR is no longer present or used since 19.1
1486 rm -rf /var/cache/ubuntu-advantage-tools
1487 # machine-access cache files no longer present or used since 20.1
1488@@ -111,10 +208,8 @@ case "$1" in
1489 # 14.04 and unsupported arch
1490 unconfigure_esm
1491 fi
1492- else
1493- # not 14.04, regardless of arch
1494- unconfigure_esm
1495 fi
1496+
1497 if [ ! -f /var/log/ubuntu-advantage.log ]; then
1498 touch /var/log/ubuntu-advantage.log
1499 fi
1500@@ -124,6 +219,13 @@ case "$1" in
1501 if [ -d "$private_dir" ]; then
1502 chmod 0700 "$private_dir"
1503 fi
1504+
1505+ if [ "$VERSION_ID" = "16.04" ]; then
1506+ if echo "$PREVIOUS_PKG_VER" | grep -q "14.04"; then
1507+ mark_reboot_cmds_as_needed MESSAGE_LIVEPATCH_LTS_REBOOT_REQUIRED
1508+ fi
1509+ fi
1510+ mark_reboot_for_fips_pro
1511 ;;
1512 esac
1513
1514diff --git a/debian/postrm b/debian/postrm
1515index 2112cf5..fb84664 100644
1516--- a/debian/postrm
1517+++ b/debian/postrm
1518@@ -16,11 +16,10 @@ remove_logs(){
1519 rm -f /var/log/ubuntu-advantage.log*
1520 }
1521
1522-remove_esm(){
1523- # config files created in postinst, need explicit handling on purge
1524+remove_gpg_files(){
1525 rm -f /etc/apt/trusted.gpg.d/ubuntu-advantage-esm-infra-trusty.gpg
1526- rm -f /etc/apt/sources.list.d/ubuntu-esm-infra-trusty.list
1527- rm -f /etc/apt/preferences.d/ubuntu-esm-infra-trusty
1528+ rm -f /etc/apt/trusted.gpg.d/ubuntu-advantage-esm-apps.gpg
1529+ rm -f /etc/apt/trusted.gpg.d/ubuntu-advantage-fips.gpg
1530 }
1531
1532 case "$1" in
1533@@ -28,7 +27,7 @@ case "$1" in
1534 remove_apt_auth
1535 remove_cache_dir
1536 remove_logs
1537- remove_esm
1538+ remove_gpg_files
1539 ;;
1540 esac
1541
1542diff --git a/debian/prerm b/debian/prerm
1543index 4644c05..59b2b1b 100644
1544--- a/debian/prerm
1545+++ b/debian/prerm
1546@@ -5,9 +5,9 @@ set -e
1547
1548 remove_apt_files() {
1549 python3 -c '
1550-from uaclient.apt import clean_apt_sources
1551+from uaclient.apt import clean_apt_files
1552
1553-clean_apt_sources()
1554+clean_apt_files()
1555 '
1556
1557 }
1558diff --git a/debian/rules b/debian/rules
1559index a535bbb..888dcde 100755
1560--- a/debian/rules
1561+++ b/debian/rules
1562@@ -39,26 +39,24 @@ endif
1563 endif
1564
1565 override_dh_gencontrol:
1566- [ -z '$(APT_PKG_DEPS)' ] || echo extra:Depends=$(APT_PKG_DEPS) >> debian/ubuntu-advantage-tools.substvars
1567+ echo extra:Depends=$(APT_PKG_DEPS) >> debian/ubuntu-advantage-tools.substvars
1568 dh_gencontrol
1569
1570 override_dh_auto_install:
1571 dh_auto_install --destdir=debian/ubuntu-advantage-tools
1572 flist=$$(find $(CURDIR)/debian/ -type f -name version.py) && sed -i 's,@@PACKAGED_VERSION@@,$(DEB_VERSION),' $${flist:-did-not-find-version-py-for-replacement}
1573-ifeq (${VERSION_ID},"14.04")
1574 make -C apt-hook DESTDIR=$(CURDIR)/debian/ubuntu-advantage-tools install
1575-endif
1576
1577 ifeq (${VERSION_ID},"14.04")
1578- # Move ua-auto-attach.conf to ubuntu-advantage-pro
1579+ # Move ua-auto-attach.conf out to ubuntu-advantage-pro
1580 mkdir -p debian/ubuntu-advantage-pro/etc/init
1581 mv debian/ubuntu-advantage-tools/etc/init/ua-auto-attach.conf debian/ubuntu-advantage-pro/etc/init/
1582 rmdir debian/ubuntu-advantage-tools/etc/init
1583 else
1584- # Move ua-auto-attach.service to ubuntu-advantage-pro
1585+ # Move ua-auto-attach.service out to ubuntu-advantage-pro
1586 mkdir -p debian/ubuntu-advantage-pro/lib/systemd/system
1587- mv debian/ubuntu-advantage-tools/lib/systemd/system/ua-auto-attach.service debian/ubuntu-advantage-pro/lib/systemd/system
1588- cd debian/ubuntu-advantage-tools && rmdir -p lib/systemd/system
1589+ mv debian/ubuntu-advantage-tools/lib/systemd/system/ua-auto-attach.* debian/ubuntu-advantage-pro/lib/systemd/system
1590+ cd debian/ubuntu-advantage-tools
1591 endif
1592
1593 override_dh_auto_clean:
1594diff --git a/debian/ubuntu-advantage-tools.templates b/debian/ubuntu-advantage-tools.templates
1595index 255d330..1b5c6f8 100644
1596--- a/debian/ubuntu-advantage-tools.templates
1597+++ b/debian/ubuntu-advantage-tools.templates
1598@@ -1,6 +1,6 @@
1599 Template: ubuntu-advantage-tools/suggest_pro_pkg
1600 Type: note
1601-Description: Ubuntu Pro support now requires ubuntu-advantage-pro
1602+_Description: Ubuntu Pro support now requires ubuntu-advantage-pro
1603 To install run the following:
1604 .
1605 sudo apt-get install ubuntu-advantage-pro
1606diff --git a/features/attach_invalidtoken.feature b/features/attach_invalidtoken.feature
1607index 11a8ba8..4f3fc9a 100644
1608--- a/features/attach_invalidtoken.feature
1609+++ b/features/attach_invalidtoken.feature
1610@@ -1,15 +1,22 @@
1611 Feature: Command behaviour when trying to attach a machine to an Ubuntu
1612 Advantage subscription using an invalid token
1613
1614- Scenario: Attach command in a trusty lxd container
1615- Given a trusty lxd container with ubuntu-advantage-tools installed
1616- When I run `ua attach INVALID_TOKEN` with sudo
1617- Then I will see the following on stderr:
1618+ @series.all
1619+ Scenario Outline: Attach command in a machine
1620+ Given a `<release>` machine with ubuntu-advantage-tools installed
1621+ When I verify that running `ua attach INVALID_TOKEN` `with sudo` exits `1`
1622+ Then stderr matches regexp:
1623 """
1624 Invalid token. See https://ubuntu.com/advantage
1625 """
1626- When I run `ua attach INVALID_TOKEN` as non-root
1627+ When I verify that running `ua attach INVALID_TOKEN` `as non-root` exits `1`
1628 Then I will see the following on stderr:
1629 """
1630 This command must be run as root (try using sudo)
1631 """
1632+ Examples: ubuntu release
1633+ | release |
1634+ | trusty |
1635+ | xenial |
1636+ | bionic |
1637+ | focal |
1638diff --git a/features/attach_validtoken.feature b/features/attach_validtoken.feature
1639index 2a7bfc9..a25234f 100644
1640--- a/features/attach_validtoken.feature
1641+++ b/features/attach_validtoken.feature
1642@@ -1,10 +1,38 @@
1643+@uses.config.contract_token
1644 Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
1645 subscription using a valid token
1646
1647- @uses.config.contract_token
1648- Scenario: Attach command in a trusty lxd container
1649- Given a trusty lxd container with ubuntu-advantage-tools installed
1650- When I attach contract_token with sudo
1651+ @series.all
1652+ @uses.config.machine_type.lxd.container
1653+ Scenario Outline: Attach command in a ubuntu lxd container
1654+ Given a `<release>` machine with ubuntu-advantage-tools installed
1655+ When I run `apt-get update` with sudo, retrying exit [100]
1656+ And I run `apt-get install -y <downrev_pkg>` with sudo, retrying exit [100]
1657+ When I verify that running ` --assume-yes --beta` `with sudo` exits `1`
1658+ And I run `/usr/lib/update-notifier/apt-check --human-readable` as non-root
1659+ Then if `<release>` in `trusty` and stdout matches regexp:
1660+ """
1661+ UA Infrastructure Extended Security Maintenance \(ESM\) is not enabled.
1662+
1663+ \d+ update(s)? can be installed immediately.
1664+ \d+ of these updates (is a|are) security update(s)?.
1665+ """
1666+ Then if `<release>` in `trusty` and stdout matches regexp:
1667+ """
1668+ Enable UA Infrastructure ESM to receive \d+ additional security update(s)?.
1669+ See https://ubuntu.com/advantage or run: sudo ua status
1670+ """
1671+ Then if `<release>` in `xenial or bionic` and stdout matches regexp:
1672+ """
1673+ \d+ package(s)? can be updated.
1674+ \d+ of these updates (is a|are) security update(s)?.
1675+ """
1676+ Then if `<release>` in `focal` and stdout matches regexp:
1677+ """
1678+ \d+ update(s)? can be installed immediately.
1679+ \d+ of these updates (is a|are) security update(s)?.
1680+ """
1681+ When I attach `contract_token` with sudo
1682 Then stdout matches regexp:
1683 """
1684 ESM Infra enabled
1685@@ -16,15 +44,332 @@ Feature: Command behaviour when attaching a machine to an Ubuntu Advantage
1686 And stdout matches regexp:
1687 """
1688 SERVICE ENTITLED STATUS DESCRIPTION
1689- cc-eal +yes +n/a +Common Criteria EAL2 Provisioning Packages
1690- cis-audit +no +— +Center for Internet Security Audit Tools
1691- esm-apps +no +— +UA Apps: Extended Security Maintenance
1692- esm-infra +yes +enabled +UA Infra: Extended Security Maintenance
1693+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1694 fips +yes +n/a +NIST-certified FIPS modules
1695 fips-updates +yes +n/a +Uncertified security updates to FIPS modules
1696 livepatch +yes +n/a +Canonical Livepatch service
1697 """
1698- And I will see the following on stderr:
1699+ And stderr matches regexp:
1700+ """
1701+ Enabling default service esm-infra
1702+ """
1703+ When I run `/usr/lib/update-notifier/apt-check --human-readable` as non-root
1704+ Then if `<release>` in `trusty` and stdout matches regexp:
1705+ """
1706+ UA (Infra:|Infrastructure) Extended Security Maintenance \(ESM\) is enabled.
1707+
1708+ \d+ update(s)? can be installed immediately.
1709+ \d+ of these updates (is|are) (fixed|provided) through UA (Infra:|Infrastructure) ESM.
1710+ \d+ of these updates (is a|are) security update(s)?.
1711+ """
1712+ Then if `<release>` in `focal` and stdout matches regexp:
1713+ """
1714+ UA (Infra:|Infrastructure) Extended Security Maintenance \(ESM\) is enabled.
1715+
1716+ \d+ update(s)? can be installed immediately.
1717+ \d+ of these updates (is|are) (fixed|provided) through UA (Infra:|Infrastructure) ESM.
1718+ \d+ of these updates (is a|are) security update(s)?.
1719+ To see these additional updates run: apt list --upgradable
1720+ """
1721+ Then if `<release>` in `xenial or bionic` and stdout matches regexp:
1722+ """
1723+ \d+ package(s)? can be updated.
1724+ \d+ of these updates (is a|are) security update(s)?.
1725+ """
1726+ Examples: ubuntu release packages
1727+ | release | downrev_pkg |
1728+ | trusty | libgit2-0=0.19.0-2 |
1729+ | xenial | libkrad0=1.13.2+dfsg-5 |
1730+ | bionic | libkrad0=1.16-2build1 |
1731+ | focal | hello=2.10-2ubuntu2 |
1732+
1733+ @series.all
1734+ @uses.config.machine_type.aws.generic
1735+ Scenario Outline: Attach command in a ubuntu lxd container
1736+ Given a `<release>` machine with ubuntu-advantage-tools installed
1737+ When I attach `contract_token` with sudo
1738+ Then stdout matches regexp:
1739+ """
1740+ ESM Infra enabled
1741+ """
1742+ And stdout matches regexp:
1743+ """
1744+ This machine is now attached to
1745+ """
1746+ And stdout matches regexp:
1747+ """
1748+ SERVICE ENTITLED STATUS DESCRIPTION
1749+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1750+ fips +yes +<fips_status> +NIST-certified FIPS modules
1751+ fips-updates +yes +<fips_status> +Uncertified security updates to FIPS modules
1752+ livepatch +yes +<lp_status> +<lp_desc>
1753+ """
1754+ And stderr matches regexp:
1755+ """
1756+ Enabling default service esm-infra
1757+ """
1758+
1759+ Examples: ubuntu release livepatch status
1760+ | release | fips_status |lp_status | lp_desc |
1761+ | trusty | n/a |n/a | Available with the HWE kernel |
1762+ | xenial | disabled |enabled | Canonical Livepatch service |
1763+ | bionic | disabled |enabled | Canonical Livepatch service |
1764+ | focal | n/a |enabled | Canonical Livepatch service |
1765+
1766+ @series.all
1767+ @uses.config.machine_type.azure.generic
1768+ Scenario Outline: Attach command in a ubuntu lxd container
1769+ Given a `<release>` machine with ubuntu-advantage-tools installed
1770+ When I attach `contract_token` with sudo
1771+ Then stdout matches regexp:
1772+ """
1773+ ESM Infra enabled
1774+ """
1775+ And stdout matches regexp:
1776+ """
1777+ This machine is now attached to
1778+ """
1779+ And stdout matches regexp:
1780+ """
1781+ SERVICE ENTITLED STATUS DESCRIPTION
1782+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1783+ fips +yes +<fips_status> +NIST-certified FIPS modules
1784+ fips-updates +yes +<fips_status> +Uncertified security updates to FIPS modules
1785+ livepatch +yes +<lp_status> +Canonical Livepatch service
1786+ """
1787+ And stderr matches regexp:
1788+ """
1789+ Enabling default service esm-infra
1790+ """
1791+
1792+ Examples: ubuntu release livepatch status
1793+ | release | lp_status | fips_status |
1794+ | trusty | disabled | n/a |
1795+ | xenial | n/a | n/a |
1796+ | bionic | n/a | disabled |
1797+ | focal | n/a | n/a |
1798+
1799+ @series.all
1800+ @uses.config.machine_type.gcp.generic
1801+ Scenario Outline: Attach command in a ubuntu lxd container
1802+ Given a `<release>` machine with ubuntu-advantage-tools installed
1803+ When I attach `contract_token` with sudo
1804+ Then stdout matches regexp:
1805+ """
1806+ ESM Infra enabled
1807+ """
1808+ And stdout matches regexp:
1809+ """
1810+ This machine is now attached to
1811+ """
1812+ And stdout matches regexp:
1813+ """
1814+ SERVICE ENTITLED STATUS DESCRIPTION
1815+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
1816+ fips +yes +<fips_status> +NIST-certified FIPS modules
1817+ fips-updates +yes +<fips_status> +Uncertified security updates to FIPS modules
1818+ livepatch +yes +<lp_status> +Canonical Livepatch service
1819+ """
1820+ And stderr matches regexp:
1821 """
1822 Enabling default service esm-infra
1823 """
1824+
1825+ Examples: ubuntu release livepatch status
1826+ | release | lp_status | fips_status |
1827+ | trusty | disabled | n/a |
1828+ | xenial | n/a | n/a |
1829+ | bionic | n/a | n/a |
1830+ | focal | n/a | n/a |
1831+
1832+ @series.bionic
1833+ @uses.config.machine_type.azure.generic
1834+ Scenario Outline: Attached enable of vm-based services in an ubuntu lxd vm
1835+ Given a `<release>` machine with ubuntu-advantage-tools installed
1836+ When I attach `contract_token_staging` with sudo
1837+ And I run `apt-get install openssh-client openssh-server strongswan -y` with sudo
1838+ And I run `apt-mark hold openssh-client openssh-server strongswan` with sudo
1839+ And I run `ua enable <fips-service> --assume-yes` with sudo
1840+ Then stdout matches regexp:
1841+ """
1842+ Updating package lists
1843+ Installing <fips-name> packages
1844+ <fips-name> enabled
1845+ A reboot is required to complete install
1846+ """
1847+ When I run `ua status --all` with sudo
1848+ Then stdout matches regexp:
1849+ """
1850+ <fips-service> +yes enabled
1851+ """
1852+ And I verify that running `apt update` `with sudo` exits `0`
1853+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
1854+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
1855+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
1856+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
1857+ And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
1858+ And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
1859+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
1860+ When I run `apt-cache policy ubuntu-azure-fips` as non-root
1861+ Then stdout does not match regexp:
1862+ """
1863+ .*Installed: \(none\)
1864+ """
1865+ When I reboot the `<release>` machine
1866+ And I run `uname -r` as non-root
1867+ Then stdout matches regexp:
1868+ """
1869+ azure-fips
1870+ """
1871+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
1872+ Then I will see the following on stdout:
1873+ """
1874+ 1
1875+ """
1876+ When I run `ua disable <fips-service> --assume-yes` with sudo
1877+ Then stdout matches regexp:
1878+ """
1879+ Updating package lists
1880+ """
1881+ When I run `apt-cache policy ubuntu-azure-fips` as non-root
1882+ Then stdout matches regexp:
1883+ """
1884+ .*Installed: \(none\)
1885+ """
1886+ When I reboot the `<release>` machine
1887+ Then I verify that `openssh-server` installed version matches regexp `fips`
1888+ And I verify that `openssh-client` installed version matches regexp `fips`
1889+ And I verify that `strongswan` installed version matches regexp `fips`
1890+ And I verify that `openssh-server-hmac` installed version matches regexp `fips`
1891+ And I verify that `openssh-client-hmac` installed version matches regexp `fips`
1892+ And I verify that `strongswan-hmac` installed version matches regexp `fips`
1893+ When I run `apt-mark unhold openssh-client openssh-server strongswan` with sudo
1894+ Then I will see the following on stdout:
1895+ """
1896+ openssh-client was already not hold.
1897+ openssh-server was already not hold.
1898+ strongswan was already not hold.
1899+ """
1900+ When I run `ua status --all` with sudo
1901+ Then stdout matches regexp:
1902+ """
1903+ <fips-service> +yes disabled
1904+ """
1905+
1906+ Examples: ubuntu release
1907+ | release | fips-name | fips-service |fips-apt-source |
1908+ | bionic | FIPS | fips |https://esm.staging.ubuntu.com/fips/ubuntu bionic/main |
1909+
1910+ @series.bionic
1911+ @uses.config.machine_type.aws.generic
1912+ Scenario Outline: Attached enable of vm-based services in an ubuntu lxd vm
1913+ Given a `<release>` machine with ubuntu-advantage-tools installed
1914+ When I attach `contract_token_staging` with sudo
1915+ And I run `ua disable livepatch` with sudo
1916+ And I run `apt-get install openssh-client openssh-server strongswan -y` with sudo
1917+ And I run `apt-mark hold openssh-client openssh-server strongswan` with sudo
1918+ And I run `ua enable <fips-service> --assume-yes` with sudo
1919+ Then stdout matches regexp:
1920+ """
1921+ Updating package lists
1922+ Installing <fips-name> packages
1923+ <fips-name> enabled
1924+ A reboot is required to complete install
1925+ """
1926+ When I run `ua status --all` with sudo
1927+ Then stdout matches regexp:
1928+ """
1929+ <fips-service> +yes enabled
1930+ """
1931+ And I verify that running `apt update` `with sudo` exits `0`
1932+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
1933+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
1934+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
1935+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
1936+ And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
1937+ And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
1938+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
1939+ When I run `apt-cache policy ubuntu-aws-fips` as non-root
1940+ Then stdout does not match regexp:
1941+ """
1942+ .*Installed: \(none\)
1943+ """
1944+ When I reboot the `<release>` machine
1945+ And I run `uname -r` as non-root
1946+ Then stdout matches regexp:
1947+ """
1948+ aws-fips
1949+ """
1950+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
1951+ Then I will see the following on stdout:
1952+ """
1953+ 1
1954+ """
1955+ When I run `ua disable <fips-service> --assume-yes` with sudo
1956+ Then stdout matches regexp:
1957+ """
1958+ Updating package lists
1959+ """
1960+ When I run `apt-cache policy ubuntu-aws-fips` as non-root
1961+ Then stdout matches regexp:
1962+ """
1963+ .*Installed: \(none\)
1964+ """
1965+ When I reboot the `<release>` machine
1966+ Then I verify that `openssh-server` installed version matches regexp `fips`
1967+ And I verify that `openssh-client` installed version matches regexp `fips`
1968+ And I verify that `strongswan` installed version matches regexp `fips`
1969+ And I verify that `openssh-server-hmac` installed version matches regexp `fips`
1970+ And I verify that `openssh-client-hmac` installed version matches regexp `fips`
1971+ And I verify that `strongswan-hmac` installed version matches regexp `fips`
1972+ When I run `apt-mark unhold openssh-client openssh-server strongswan` with sudo
1973+ Then I will see the following on stdout:
1974+ """
1975+ openssh-client was already not hold.
1976+ openssh-server was already not hold.
1977+ strongswan was already not hold.
1978+ """
1979+ When I run `ua status --all` with sudo
1980+ Then stdout matches regexp:
1981+ """
1982+ <fips-service> +yes disabled
1983+ """
1984+
1985+ Examples: ubuntu release
1986+ | release | fips-name | fips-service |fips-apt-source |
1987+ | bionic | FIPS | fips |https://esm.staging.ubuntu.com/fips/ubuntu bionic/main |
1988+
1989+ @series.xenial
1990+ @uses.config.machine_type.azure.generic
1991+ Scenario Outline: Attached enable of vm-based services in an ubuntu lxd vm
1992+ Given a `xenial` machine with ubuntu-advantage-tools installed
1993+ When I attach `contract_token_staging` with sudo
1994+ Then I verify that running `ua enable <fips_service> --assume-yes` `with sudo` exits `1`
1995+ And stdout matches regexp:
1996+ """
1997+ Ubuntu Xenial does not provide an Azure optimized FIPS kernel
1998+ """
1999+
2000+ Examples: fips
2001+ | fips_service |
2002+ | fips |
2003+ | fips-updates |
2004+
2005+ @series.bionic
2006+ @series.xenial
2007+ @uses.config.machine_type.gcp.generic
2008+ Scenario Outline: Attached enable of fips services in an ubuntu gcp vm
2009+ Given a `<release>` machine with ubuntu-advantage-tools installed
2010+ When I attach `contract_token_staging` with sudo
2011+ Then I verify that running `ua enable <fips_service> --assume-yes` `with sudo` exits `1`
2012+ And stdout matches regexp:
2013+ """
2014+ Ubuntu <release_title> does not provide a GCP optimized FIPS kernel
2015+ """
2016+
2017+ Examples: fips
2018+ | release | release_title | fips_service |
2019+ | xenial | Xenial | fips |
2020+ | xenial | Xenial | fips-updates |
2021+ | bionic | Bionic | fips |
2022+ | bionic | Bionic | fips-updates |
2023diff --git a/features/attached_commands.feature b/features/attached_commands.feature
2024index 24dfbfe..4b48f76 100644
2025--- a/features/attached_commands.feature
2026+++ b/features/attached_commands.feature
2027@@ -1,13 +1,14 @@
2028 @uses.config.contract_token
2029 Feature: Command behaviour when attached to an UA subscription
2030
2031- Scenario: Attached refresh in a trusty lxd container
2032- Given a trusty lxd container with ubuntu-advantage-tools installed
2033- When I attach contract_token with sudo
2034- And I run `ua refresh` as non-root
2035- Then I will see the following on stderr:
2036+ @series.all
2037+ Scenario Outline: Attached refresh in a ubuntu machine
2038+ Given a `<release>` machine with ubuntu-advantage-tools installed
2039+ When I attach `contract_token` with sudo
2040+ Then I verify that running `ua refresh` `as non-root` exits `1`
2041+ And stderr matches regexp:
2042 """
2043- This command must be run as root (try using sudo)
2044+ This command must be run as root \(try using sudo\)
2045 """
2046 When I run `ua refresh` with sudo
2047 Then I will see the following on stdout:
2048@@ -15,43 +16,55 @@ Feature: Command behaviour when attached to an UA subscription
2049 Successfully refreshed your subscription
2050 """
2051
2052- Scenario: Attached disable of an already disabled service in a trusty lxd container
2053- Given a trusty lxd container with ubuntu-advantage-tools installed
2054- When I attach contract_token with sudo
2055- And I run `ua disable livepatch` as non-root
2056- Then I will see the following on stderr:
2057+ Examples: ubuntu release
2058+ | release |
2059+ | bionic |
2060+ | focal |
2061+ | trusty |
2062+ | xenial |
2063+
2064+ @series.all
2065+ Scenario Outline: Attached disable of an already disabled service in a ubuntu machine
2066+ Given a `<release>` machine with ubuntu-advantage-tools installed
2067+ When I attach `contract_token` with sudo
2068+ Then I verify that running `ua disable livepatch` `as non-root` exits `1`
2069+ And stderr matches regexp:
2070 """
2071- This command must be run as root (try using sudo)
2072+ This command must be run as root \(try using sudo\)
2073 """
2074- When I run `ua disable livepatch` with sudo
2075- Then I will see the following on stdout:
2076+ And I verify that running `ua disable livepatch` `with sudo` exits `1`
2077+ And I will see the following on stdout:
2078 """
2079 Livepatch is not currently enabled
2080 See: sudo ua status
2081 """
2082
2083- Scenario: Attached disable of an unknown service in a trusty lxd container
2084- Given a trusty lxd container with ubuntu-advantage-tools installed
2085- When I attach contract_token with sudo
2086- And I run `ua disable foobar` as non-root
2087- Then I will see the following on stderr:
2088+ Examples: ubuntu release
2089+ | release |
2090+ | bionic |
2091+ | focal |
2092+ | trusty |
2093+ | xenial |
2094+
2095+ @series.all
2096+ Scenario Outline: Attached disable of a service in a ubuntu machine
2097+ Given a `<release>` machine with ubuntu-advantage-tools installed
2098+ When I attach `contract_token` with sudo
2099+ Then I verify that running `ua disable foobar` `as non-root` exits `1`
2100+ And stderr matches regexp:
2101 """
2102- This command must be run as root (try using sudo)
2103+ This command must be run as root \(try using sudo\)
2104 """
2105- When I run `ua disable foobar` with sudo
2106- Then I will see the following on stderr:
2107+ And I verify that running `ua disable foobar` `with sudo` exits `1`
2108+ And stderr matches regexp:
2109 """
2110- Cannot disable 'foobar'
2111- For a list of services see: sudo ua status
2112+ Cannot disable unknown service 'foobar'.
2113+ Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch
2114 """
2115-
2116- Scenario: Attached disable of an already enabled service in a trusty lxd container
2117- Given a trusty lxd container with ubuntu-advantage-tools installed
2118- When I attach contract_token with sudo
2119- And I run `ua disable esm-infra` as non-root
2120- Then I will see the following on stderr:
2121+ And I verify that running `ua disable esm-infra` `as non-root` exits `1`
2122+ And stderr matches regexp:
2123 """
2124- This command must be run as root (try using sudo)
2125+ This command must be run as root \(try using sudo\)
2126 """
2127 When I run `ua disable esm-infra` with sudo
2128 Then I will see the following on stdout:
2129@@ -61,26 +74,24 @@ Feature: Command behaviour when attached to an UA subscription
2130 When I run `ua status` with sudo
2131 Then stdout matches regexp:
2132 """
2133- esm-infra +yes +disabled +UA Infra: Extended Security Maintenance
2134- """
2135- When I run `apt-cache policy` with sudo
2136- Then stdout matches regexp:
2137- """
2138- -32768 https://esm.ubuntu.com/ubuntu/ trusty-infra-updates/main amd64 Packages
2139- """
2140- And stdout matches regexp:
2141- """
2142- -32768 https://esm.ubuntu.com/ubuntu/ trusty-infra-security/main amd64 Packages
2143+ esm-infra +yes +disabled +UA Infra: Extended Security Maintenance \(ESM\)
2144 """
2145+ And I verify that running `apt update` `with sudo` exits `0`
2146
2147+ Examples: ubuntu release
2148+ | release |
2149+ | bionic |
2150+ | focal |
2151+ | xenial |
2152
2153- Scenario: Attached detach in a trusty lxd container
2154- Given a trusty lxd container with ubuntu-advantage-tools installed
2155- When I attach contract_token with sudo
2156- And I run `ua detach` as non-root
2157- Then I will see the following on stderr:
2158+ @series.all
2159+ Scenario Outline: Attached detach in a trusty machine
2160+ Given a `<release>` machine with ubuntu-advantage-tools installed
2161+ When I attach `contract_token` with sudo
2162+ Then I verify that running `ua detach` `as non-root` exits `1`
2163+ And stderr matches regexp:
2164 """
2165- This command must be run as root (try using sudo)
2166+ This command must be run as root \(try using sudo\)
2167 """
2168 When I run `ua detach --assume-yes` with sudo
2169 Then I will see the following on stdout:
2170@@ -90,18 +101,336 @@ Feature: Command behaviour when attached to an UA subscription
2171 Updating package lists
2172 This machine is now detached
2173 """
2174- When I run `ua status` as non-root
2175+ When I run `ua status --all` as non-root
2176 Then stdout matches regexp:
2177 """
2178 SERVICE AVAILABLE DESCRIPTION
2179- cc-eal +no +Common Criteria EAL2 Provisioning Packages
2180- esm-apps +no +UA Apps: Extended Security Maintenance
2181- esm-infra +yes +UA Infra: Extended Security Maintenance
2182- fips +no +NIST-certified FIPS modules
2183- fips-updates +no +Uncertified security updates to FIPS modules
2184+ cc-eal +<cc-eal> +Common Criteria EAL2 Provisioning Packages
2185+ cis +<cis> +Center for Internet Security Audit Tools
2186+ esm-apps +<esm-apps> +UA Apps: Extended Security Maintenance \(ESM\)
2187+ esm-infra +yes +UA Infra: Extended Security Maintenance \(ESM\)
2188+ fips +<fips> +NIST-certified FIPS modules
2189+ fips-updates +<fips> +Uncertified security updates to FIPS modules
2190 livepatch +yes +Canonical Livepatch service
2191 """
2192 And stdout matches regexp:
2193 """
2194 This machine is not attached to a UA subscription.
2195 """
2196+ And I verify that running `apt update` `with sudo` exits `0`
2197+
2198+ Examples: ubuntu release
2199+ | release | esm-apps | cc-eal | cis | fips | fips-update |
2200+ | bionic | yes | no | yes | yes | yes |
2201+ | focal | yes | no | no | no | no |
2202+ | trusty | no | no | no | no | no |
2203+ | xenial | yes | yes | yes | yes | yes |
2204+
2205+ @series.all
2206+ Scenario Outline: Attached auto-attach in a ubuntu machine
2207+ Given a `<release>` machine with ubuntu-advantage-tools installed
2208+ When I attach `contract_token` with sudo
2209+ Then I verify that running `ua auto-attach` `as non-root` exits `1`
2210+ And stderr matches regexp:
2211+ """
2212+ This command must be run as root \(try using sudo\)
2213+ """
2214+ When I run `ua auto-attach` with sudo
2215+ Then stderr matches regexp:
2216+ """
2217+ This machine is already attached
2218+ """
2219+
2220+ Examples: ubuntu release
2221+ | release |
2222+ | bionic |
2223+ | focal |
2224+ | trusty |
2225+ | xenial |
2226+
2227+ @series.all
2228+ Scenario Outline: Attached show version in a ubuntu machine
2229+ Given a `<release>` machine with ubuntu-advantage-tools installed
2230+ When I attach `contract_token` with sudo
2231+ And I run `ua version` as non-root
2232+ Then I will see the uaclient version on stdout
2233+ When I run `ua version` with sudo
2234+ Then I will see the uaclient version on stdout
2235+ When I run `ua --version` as non-root
2236+ Then I will see the uaclient version on stdout
2237+ When I run `ua --version` with sudo
2238+ Then I will see the uaclient version on stdout
2239+
2240+ Examples: ubuntu release
2241+ | release |
2242+ | bionic |
2243+ | focal |
2244+ | trusty |
2245+ | xenial |
2246+
2247+ @series.all
2248+ Scenario Outline: Unattached status in a ubuntu machine with feature overrides
2249+ Given a `<release>` machine with ubuntu-advantage-tools installed
2250+ When I create the file `/tmp/machine-token-overlay.json` with the following:
2251+ """
2252+ {
2253+ "machineTokenInfo": {
2254+ "contractInfo": {
2255+ "resourceEntitlements": [
2256+ {
2257+ "type": "cc-eal",
2258+ "entitled": false
2259+ }
2260+ ]
2261+ }
2262+ }
2263+ }
2264+ """
2265+ And I append the following on uaclient config:
2266+ """
2267+ features:
2268+ machine_token_overlay: "/tmp/machine-token-overlay.json"
2269+ disable_auto_attach: true
2270+ other: false
2271+ """
2272+ And I attach `contract_token` with sudo
2273+ And I run `ua status --all` with sudo
2274+ Then stdout matches regexp:
2275+ """
2276+ SERVICE ENTITLED STATUS DESCRIPTION
2277+ cc-eal no
2278+ """
2279+ When I run `ua --version` as non-root
2280+ Then I will see the uaclient version on stdout with features ` +disable_auto_attach +machine_token_overlay -other`
2281+ When I run `ua version` as non-root
2282+ Then I will see the uaclient version on stdout with features ` +disable_auto_attach +machine_token_overlay -other`
2283+ When I run `ua auto-attach` with sudo
2284+ Then stdout matches regexp:
2285+ """
2286+ Skipping auto-attach. Config disable_auto_attach is set.
2287+ """
2288+
2289+ Examples: ubuntu release
2290+ | release |
2291+ | bionic |
2292+ | focal |
2293+ | trusty |
2294+ | xenial |
2295+
2296+ @series.all
2297+ Scenario Outline: Attached disable of different services in a ubuntu machine
2298+ Given a `<release>` machine with ubuntu-advantage-tools installed
2299+ When I attach `contract_token` with sudo
2300+ Then I verify that running `ua disable esm-infra livepatch foobar` `as non-root` exits `1`
2301+ And stderr matches regexp:
2302+ """
2303+ This command must be run as root \(try using sudo\)
2304+ """
2305+ And I verify that running `ua disable esm-infra livepatch foobar` `with sudo` exits `1`
2306+ And I will see the following on stdout:
2307+ """
2308+ Updating package lists
2309+ Livepatch is not currently enabled
2310+ See: sudo ua status
2311+ """
2312+ And stderr matches regexp:
2313+ """
2314+ Cannot disable unknown service 'foobar'.
2315+ Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch
2316+ """
2317+ When I run `ua status` with sudo
2318+ Then stdout matches regexp:
2319+ """
2320+ esm-infra +yes +disabled +UA Infra: Extended Security Maintenance \(ESM\)
2321+ """
2322+
2323+ Examples: ubuntu release
2324+ | release |
2325+ | bionic |
2326+ | focal |
2327+ | trusty |
2328+ | xenial |
2329+
2330+ @series.trusty
2331+ Scenario: Attached disable of an already enabled service in a trusty machine
2332+ Given a `trusty` machine with ubuntu-advantage-tools installed
2333+ When I attach `contract_token` with sudo
2334+ Then I verify that running `ua disable foobar` `as non-root` exits `1`
2335+ And stderr matches regexp:
2336+ """
2337+ This command must be run as root \(try using sudo\)
2338+ """
2339+ And I verify that running `ua disable foobar` `with sudo` exits `1`
2340+ And stderr matches regexp:
2341+ """
2342+ Cannot disable unknown service 'foobar'.
2343+ Try cc-eal, cis, esm-apps, esm-infra, fips, fips-updates, livepatch
2344+ """
2345+ And I verify that running `ua disable esm-infra` `as non-root` exits `1`
2346+ And stderr matches regexp:
2347+ """
2348+ This command must be run as root \(try using sudo\)
2349+ """
2350+ When I run `ua disable esm-infra` with sudo
2351+ Then I will see the following on stdout:
2352+ """
2353+ Updating package lists
2354+ """
2355+ When I run `ua status` with sudo
2356+ Then stdout matches regexp:
2357+ """
2358+ esm-infra +yes +disabled +UA Infra: Extended Security Maintenance \(ESM\)
2359+ """
2360+ And I verify that running `apt update` `with sudo` exits `0`
2361+ When I run `apt-cache policy` with sudo
2362+ Then apt-cache policy for the following url has permission `-32768`
2363+ """
2364+ https://esm.ubuntu.com/ubuntu/ trusty-infra-security/main amd64 Packages
2365+ """
2366+ And apt-cache policy for the following url has permission `-32768`
2367+ """
2368+ https://esm.ubuntu.com/ubuntu/ trusty-infra-updates/main amd64 Packages
2369+ """
2370+
2371+ @series.all
2372+ Scenario Outline: Help command on an attached machine
2373+ Given a `<release>` machine with ubuntu-advantage-tools installed
2374+ When I attach `contract_token` with sudo
2375+ And I run `ua help esm-infra` with sudo
2376+ Then I will see the following on stdout:
2377+ """
2378+ Name:
2379+ esm-infra
2380+
2381+ Entitled:
2382+ yes
2383+
2384+ Status:
2385+ enabled
2386+
2387+ Help:
2388+ esm-infra provides access to a private ppa which includes available high
2389+ and critical CVE fixes for Ubuntu LTS packages in the Ubuntu Main
2390+ repository between the end of the standard Ubuntu LTS security
2391+ maintenance and its end of life. It is enabled by default with
2392+ Extended Security Maintenance (ESM) for UA Apps and UA Infra.
2393+ You can find our more about the esm service at
2394+ https://ubuntu.com/security/esm
2395+ """
2396+ When I run `ua help esm-infra --format json` with sudo
2397+ Then I will see the following on stdout:
2398+ """
2399+ {"name": "esm-infra", "entitled": "yes", "status": "enabled", "help": "esm-infra provides access to a private ppa which includes available high\nand critical CVE fixes for Ubuntu LTS packages in the Ubuntu Main\nrepository between the end of the standard Ubuntu LTS security\nmaintenance and its end of life. It is enabled by default with\nExtended Security Maintenance (ESM) for UA Apps and UA Infra.\nYou can find our more about the esm service at\nhttps://ubuntu.com/security/esm\n"}
2400+ """
2401+ And I verify that running `ua help invalid-service` `with sudo` exits `1`
2402+ And I will see the following on stderr:
2403+ """
2404+ No help available for 'invalid-service'
2405+ """
2406+ When I run `ua --help` as non-root
2407+ Then stdout matches regexp:
2408+ """
2409+ Client to manage Ubuntu Advantage services on a machine.
2410+ - esm-infra: UA Infra: Extended Security Maintenance \(ESM\)
2411+ \(https://ubuntu.com/security/esm\)
2412+ - fips-updates: Uncertified security updates to FIPS modules
2413+ \(https://ubuntu.com/security/certifications#fips\)
2414+ - fips: NIST-certified FIPS modules
2415+ \(https://ubuntu.com/security/certifications#fips\)
2416+ - livepatch: Canonical Livepatch service
2417+ \(https://ubuntu.com/security/livepatch\)
2418+ """
2419+ When I run `ua help` as non-root
2420+ Then stdout matches regexp:
2421+ """
2422+ Client to manage Ubuntu Advantage services on a machine.
2423+ - esm-infra: UA Infra: Extended Security Maintenance \(ESM\)
2424+ \(https://ubuntu.com/security/esm\)
2425+ - fips-updates: Uncertified security updates to FIPS modules
2426+ \(https://ubuntu.com/security/certifications#fips\)
2427+ - fips: NIST-certified FIPS modules
2428+ \(https://ubuntu.com/security/certifications#fips\)
2429+ - livepatch: Canonical Livepatch service
2430+ \(https://ubuntu.com/security/livepatch\)
2431+ """
2432+ When I run `ua help --all` as non-root
2433+ Then stdout matches regexp:
2434+ """
2435+ Client to manage Ubuntu Advantage services on a machine.
2436+ - cc-eal: Common Criteria EAL2 Provisioning Packages
2437+ \(https://ubuntu.com/cc-eal\)
2438+ - cis: Center for Internet Security Audit Tools
2439+ \(https://ubuntu.com/security/certifications#cis\)
2440+ - esm-apps: UA Apps: Extended Security Maintenance \(ESM\)
2441+ \(https://ubuntu.com/security/esm\)
2442+ - esm-infra: UA Infra: Extended Security Maintenance \(ESM\)
2443+ \(https://ubuntu.com/security/esm\)
2444+ - fips-updates: Uncertified security updates to FIPS modules
2445+ \(https://ubuntu.com/security/certifications#fips\)
2446+ - fips: NIST-certified FIPS modules
2447+ \(https://ubuntu.com/security/certifications#fips\)
2448+ - livepatch: Canonical Livepatch service
2449+ \(https://ubuntu.com/security/livepatch\)
2450+ """
2451+
2452+ Examples: ubuntu release
2453+ | release |
2454+ | bionic |
2455+ | focal |
2456+ | trusty |
2457+ | xenial |
2458+
2459+ @series.all
2460+ Scenario Outline: Purge package after attaching it to a machine
2461+ Given a `<release>` machine with ubuntu-advantage-tools installed
2462+ When I attach `contract_token` with sudo
2463+ And I run `touch /etc/apt/preferences.d/ubuntu-esm-infra` with sudo
2464+ Then I verify that files exist matching `/var/log/ubuntu-advantage.log`
2465+ And I verify that running `test -d /var/lib/ubuntu-advantage` `with sudo` exits `0`
2466+ And I verify that files exist matching `/etc/apt/auth.conf.d/90ubuntu-advantage`
2467+ And I verify that files exist matching `/etc/apt/trusted.gpg.d/ubuntu-advantage-esm-infra-trusty.gpg`
2468+ And I verify that files exist matching `/etc/apt/sources.list.d/ubuntu-esm-infra.list`
2469+ And I verify that files exist matching `/etc/apt/preferences.d/ubuntu-esm-infra`
2470+ When I run `apt-get purge ubuntu-advantage-tools -y` with sudo, retrying exit [100]
2471+ Then stdout matches regexp:
2472+ """
2473+ Purging configuration files for ubuntu-advantage-tools
2474+ """
2475+ And I verify that no files exist matching `/var/log/ubuntu-advantage.log`
2476+ And I verify that no files exist matching `/var/lib/ubuntu-advantage`
2477+ And I verify that no files exist matching `/etc/apt/auth.conf.d/90ubuntu-advantage`
2478+ And I verify that no files exist matching `/etc/apt/sources.list.d/ubuntu-*`
2479+ And I verify that no files exist matching `/etc/apt/trusted.gpg.d/ubuntu-advantage-*`
2480+ And I verify that no files exist matching `/etc/apt/preferences.d/ubuntu-*`
2481+
2482+ Examples: ubuntu release
2483+ | release |
2484+ | bionic |
2485+ | focal |
2486+ | trusty |
2487+ | xenial |
2488+
2489+ @series.all
2490+ Scenario Outline: Enable command with invalid repositories in user machine
2491+ Given a `<release>` machine with ubuntu-advantage-tools installed
2492+ When I attach `contract_token` with sudo
2493+ And I run `ua disable esm-infra` with sudo
2494+ And I run `add-apt-repository ppa:ua-client/staging -y` with sudo, retrying exit [1]
2495+ And I run `apt update` with sudo
2496+ And I run `sed -i 's/ubuntu/ubun/' /etc/apt/sources.list.d/<ppa_file>.list` with sudo
2497+ And I run `ua enable esm-infra` with sudo
2498+ Then stdout matches regexp:
2499+ """
2500+ One moment, checking your subscription first
2501+ Updating package lists
2502+ APT update failed.
2503+ APT update failed to read APT config for the following URL:
2504+ - http://ppa.launchpad.net/ua-client/staging/ubun
2505+ """
2506+
2507+ Examples: ubuntu release
2508+ | release | ppa_file |
2509+ | trusty | ua-client-staging-trusty |
2510+ | xenial | ua-client-ubuntu-staging-xenial |
2511+ | bionic | ua-client-ubuntu-staging-bionic |
2512+ | focal | ua-client-ubuntu-staging-focal |
2513diff --git a/features/attached_enable.feature b/features/attached_enable.feature
2514index 86f2a56..8a36fce 100644
2515--- a/features/attached_enable.feature
2516+++ b/features/attached_enable.feature
2517@@ -1,131 +1,437 @@
2518 @uses.config.contract_token
2519 Feature: Enable command behaviour when attached to an UA subscription
2520
2521+ @series.all
2522+ Scenario Outline: Attached enable Common Criteria service in a ubuntu machine
2523+ Given a `<release>` machine with ubuntu-advantage-tools installed
2524+ When I attach `contract_token` with sudo
2525+ Then I verify that running `ua enable cc-eal` `as non-root` exits `1`
2526+ And I will see the following on stderr:
2527+ """
2528+ This command must be run as root (try using sudo)
2529+ """
2530+ And I verify that running `ua enable cc-eal --beta` `with sudo` exits `1`
2531+ And I will see the following on stdout
2532+ """
2533+ One moment, checking your subscription first
2534+ <msg>
2535+ """
2536+ And I verify that running `ua enable cc-eal` `with sudo` exits `1`
2537+ And I will see the following on stdout:
2538+ """
2539+ One moment, checking your subscription first
2540+ """
2541+ And stderr matches regexp:
2542+ """
2543+ Cannot enable unknown service 'cc-eal'.
2544+ Try esm-infra, fips, fips-updates, livepatch
2545+ """
2546
2547- Scenario: Attached enable Livepatch service in a trusty lxd container
2548- Given a trusty lxd container with ubuntu-advantage-tools installed
2549- When I attach contract_token with sudo
2550- And I run `ua enable livepatch` as non-root
2551- Then I will see the following on stderr:
2552+ Examples: ubuntu release
2553+ | release | msg |
2554+ | bionic | CC EAL2 is not available for Ubuntu 18.04 LTS (Bionic Beaver). |
2555+ | focal | CC EAL2 is not available for Ubuntu 20.04 LTS (Focal Fossa). |
2556+ | trusty | CC EAL2 is not available for Ubuntu 14.04 LTS (Trusty Tahr). |
2557+
2558+ @series.all
2559+ Scenario Outline: Attached enable a disabled beta service and unknown service in a ubuntu machine
2560+ Given a `<release>` machine with ubuntu-advantage-tools installed
2561+ When I attach `contract_token` with sudo
2562+ Then I verify that running `ua enable cc-eal foobar` `as non-root` exits `1`
2563+ And I will see the following on stderr:
2564 """
2565 This command must be run as root (try using sudo)
2566 """
2567- When I run `ua enable livepatch` with sudo
2568- Then I will see the following on stdout:
2569+ And I verify that running `ua enable cc-eal foobar` `with sudo` exits `1`
2570+ And I will see the following on stdout:
2571 """
2572 One moment, checking your subscription first
2573- Cannot install Livepatch on a container
2574+ """
2575+ And stderr matches regexp:
2576+ """
2577+ Cannot enable unknown service 'foobar, cc-eal'.
2578+ Try esm-infra, fips, fips-updates, livepatch
2579 """
2580
2581+ Examples: ubuntu release
2582+ | release |
2583+ | bionic |
2584+ | focal |
2585+ | trusty |
2586+ | xenial |
2587
2588- Scenario: Attached enable Common Criteria service in a trusty lxd container
2589- Given a trusty lxd container with ubuntu-advantage-tools installed
2590- When I attach contract_token with sudo
2591- And I run `ua enable cc-eal` as non-root
2592- Then I will see the following on stderr:
2593+ @series.all
2594+ Scenario Outline: Attached enable of an unknown service in a ubuntu machine
2595+ Given a `<release>` machine with ubuntu-advantage-tools installed
2596+ When I attach `contract_token` with sudo
2597+ Then I verify that running `ua enable foobar` `as non-root` exits `1`
2598+ And I will see the following on stderr:
2599 """
2600 This command must be run as root (try using sudo)
2601 """
2602- When I run `ua enable cc-eal` with sudo
2603- Then I will see the following on stdout
2604+ And I verify that running `ua enable foobar` `with sudo` exits `1`
2605+ And I will see the following on stdout:
2606 """
2607 One moment, checking your subscription first
2608- CC EAL2 is not available for Ubuntu 14.04 LTS (Trusty Tahr).
2609+ """
2610+ And stderr matches regexp:
2611+ """
2612+ Cannot enable unknown service 'foobar'.
2613+ Try esm-infra, fips, fips-updates, livepatch
2614 """
2615
2616+ Examples: ubuntu release
2617+ | release |
2618+ | bionic |
2619+ | focal |
2620+ | trusty |
2621+ | xenial |
2622
2623- Scenario: Attached enable CIS Audit service in a trusty lxd container
2624- Given a trusty lxd container with ubuntu-advantage-tools installed
2625- When I attach contract_token with sudo
2626- And I run `ua enable cis-audit` as non-root
2627- Then I will see the following on stderr:
2628+ @series.all
2629+ Scenario Outline: Attached enable of a known service already enabled (UA Infra) in a ubuntu machine
2630+ Given a `<release>` machine with ubuntu-advantage-tools installed
2631+ When I attach `contract_token` with sudo
2632+ Then I verify that running `ua enable esm-infra` `as non-root` exits `1`
2633+ And I will see the following on stderr:
2634 """
2635 This command must be run as root (try using sudo)
2636 """
2637- When I run `ua enable cis-audit` with sudo
2638+ And I verify that running `ua enable esm-infra` `with sudo` exits `1`
2639 Then I will see the following on stdout:
2640 """
2641 One moment, checking your subscription first
2642- This subscription is not entitled to CIS Audit.
2643- For more information see: https://ubuntu.com/advantage
2644+ ESM Infra is already enabled.
2645+ See: sudo ua status
2646 """
2647+ When I run `apt-cache policy` with sudo
2648+ Then apt-cache policy for the following url has permission `500`
2649+ """
2650+ <esm-infra-url> <release>-infra-updates/main amd64 Packages
2651+ """
2652+ And I verify that running `apt update` `with sudo` exits `0`
2653+ When I run `apt install -y <infra-pkg>` with sudo, retrying exit [100]
2654+ And I run `apt-cache policy <infra-pkg>` as non-root
2655+ Then stdout matches regexp:
2656+ """
2657+ \s*500 <esm-infra-url> <release>-infra-security/main amd64 Packages
2658+ \s*500 <esm-infra-url> <release>-infra-updates/main amd64 Packages
2659+ """
2660
2661- Scenario: Attached enable UA Apps service in a trusty lxd container
2662- Given a trusty lxd container with ubuntu-advantage-tools installed
2663- When I attach contract_token with sudo
2664- And I run `ua enable esm-apps` as non-root
2665- Then I will see the following on stderr:
2666+ Examples: ubuntu release
2667+ | release | infra-pkg | esm-infra-url |
2668+ | bionic | libkrad0 | https://esm.ubuntu.com/infra/ubuntu |
2669+ | focal | hello | https://esm.ubuntu.com/infra/ubuntu |
2670+ | trusty | libgit2-0 | https://esm.ubuntu.com/ubuntu/ |
2671+ | xenial | libkrad0 | https://esm.ubuntu.com/infra/ubuntu |
2672+
2673+ @series.xenial
2674+ @series.bionic
2675+ @series.focal
2676+ Scenario Outline: Attached enable of a know service shows update in a ubuntu machine
2677+ Given a `<release>` machine with ubuntu-advantage-tools installed
2678+ When I attach `contract_token` with sudo
2679+ Then I verify that running `ua enable esm-infra` `with sudo` exits `1`
2680+ And I will see the following on stdout:
2681+ """
2682+ One moment, checking your subscription first
2683+ ESM Infra is already enabled.
2684+ See: sudo ua status
2685+ """
2686+ When I run `apt install -y <pkg-version>` with sudo, retrying exit [100]
2687+ And I run `apt update` with sudo
2688+ Then stdout matches regexp
2689+ """
2690+ \d+ of the updates (is|are) from UA Infra: ESM
2691+ """
2692+ When I run `ua disable esm-infra` with sudo
2693+ And I run `apt update` with sudo
2694+ Then stdout does not match regexp
2695+ """
2696+ \d+ of the updates (is|are) from UA Infra: ESM
2697+ """
2698+
2699+ Examples: ubuntu release
2700+ | release | pkg-version |
2701+ | bionic | libkrad0=1.16-2build1 |
2702+ | focal | hello=2.10-2ubuntu2 |
2703+ | xenial | libkrad0=1.13.2+dfsg-5 |
2704+
2705+ @series.all
2706+ @uses.config.machine_type.lxd.container
2707+ Scenario Outline: Attached enable of non-container services in a ubuntu lxd container
2708+ Given a `<release>` machine with ubuntu-advantage-tools installed
2709+ When I attach `contract_token` with sudo
2710+ Then I verify that running `ua enable <service> <flag>` `as non-root` exits `1`
2711+ And I will see the following on stderr:
2712 """
2713 This command must be run as root (try using sudo)
2714 """
2715- When I run `ua enable esm-apps` with sudo
2716- Then I will see the following on stdout:
2717+ And I verify that running `ua enable <service> <flag>` `with sudo` exits `1`
2718+ And I will see the following on stdout:
2719 """
2720 One moment, checking your subscription first
2721- This subscription is not entitled to ESM Apps.
2722- For more information see: https://ubuntu.com/advantage
2723+ Cannot install <title> on a container
2724 """
2725
2726+ Examples: Un-supported services in containers
2727+ | release | service | title | flag |
2728+ | bionic | livepatch | Livepatch | |
2729+ | bionic | fips | FIPS | --assume-yes |
2730+ | bionic | fips-updates | FIPS Updates | --assume-yes |
2731+ | focal | livepatch | Livepatch | |
2732+ | focal | fips | FIPS | --assume-yes |
2733+ | focal | fips-updates | FIPS Updates | --assume-yes |
2734+ | trusty | livepatch | Livepatch | |
2735+ | trusty | fips | FIPS | --assume-yes |
2736+ | trusty | fips-updates | FIPS Updates | --assume-yes |
2737+ | xenial | livepatch | Livepatch | |
2738+ | xenial | fips | FIPS | --assume-yes |
2739+ | xenial | fips-updates | FIPS Updates | --assume-yes |
2740
2741- Scenario: Attached enable NIST-certified FIPS service in a trusty lxd container
2742- Given a trusty lxd container with ubuntu-advantage-tools installed
2743- When I attach contract_token with sudo
2744- And I run `ua enable fips` as non-root
2745- Then I will see the following on stderr:
2746+ @series.all
2747+ Scenario Outline: Attached enable not entitled service in a ubuntu machine
2748+ Given a `<release>` machine with ubuntu-advantage-tools installed
2749+ When I attach `contract_token` with sudo
2750+ Then I verify that running `ua enable <service>` `as non-root` exits `1`
2751+ And I will see the following on stderr:
2752 """
2753 This command must be run as root (try using sudo)
2754 """
2755- When I run `ua enable fips` with sudo
2756- Then I will see the following on stdout:
2757+ And I verify that running `ua enable <service> --beta` `with sudo` exits `1`
2758+ And I will see the following on stdout:
2759 """
2760 One moment, checking your subscription first
2761- Cannot install FIPS on a container
2762+ This subscription is not entitled to <title>.
2763+ For more information see: https://ubuntu.com/advantage
2764 """
2765
2766+ Examples: not entitled services
2767+ | release | service | title |
2768+ | bionic | cis | CIS Audit |
2769+ | bionic | esm-apps | ESM Apps |
2770+ | focal | cis | CIS Audit |
2771+ | focal | esm-apps | ESM Apps |
2772+ | trusty | cis | CIS Audit |
2773+ | trusty | esm-apps | ESM Apps |
2774+ | xenial | cis | CIS Audit |
2775+ | xenial | esm-apps | ESM Apps |
2776
2777- Scenario: Attached enable Uncertified FIPS service in a trusty lxd container
2778- Given a trusty lxd container with ubuntu-advantage-tools installed
2779- When I attach contract_token with sudo
2780- And I run `ua enable fips-updates` as non-root
2781+ @series.focal
2782+ @uses.config.machine_type.lxd.vm
2783+ Scenario: Attached enable of vm-based services in a focal lxd vm
2784+ Given a `focal` machine with ubuntu-advantage-tools installed
2785+ When I attach `contract_token` with sudo
2786+ Then I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
2787+ And I will see the following on stdout:
2788+ """
2789+ One moment, checking your subscription first
2790+ FIPS is not available for Ubuntu 20.04 LTS (Focal Fossa).
2791+ """
2792+ And I verify that running `ua enable fips-updates --assume-yes` `with sudo` exits `1`
2793+ And I will see the following on stdout:
2794+ """
2795+ One moment, checking your subscription first
2796+ FIPS Updates is not available for Ubuntu 20.04 LTS (Focal Fossa).
2797+ """
2798+
2799+ @series.bionic
2800+ @series.xenial
2801+ @uses.config.machine_type.lxd.vm
2802+ Scenario Outline: Attached enable of vm-based services in a bionic lxd vm
2803+ Given a `<release>` machine with ubuntu-advantage-tools installed
2804+ When I attach `contract_token` with sudo
2805+ And I run `ua status` with sudo
2806+ Then stdout matches regexp:
2807+ """
2808+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
2809+ fips +yes +disabled +NIST-certified FIPS modules
2810+ fips-updates +yes +disabled +Uncertified security updates to FIPS modules
2811+ livepatch +yes +enabled +Canonical Livepatch service
2812+ """
2813+ When I run `ua disable livepatch` with sudo
2814+ Then I verify that running `canonical-livepatch status` `with sudo` exits `1`
2815+ And stdout matches regexp:
2816+ """
2817+ Machine is not enabled. Please run 'sudo canonical-livepatch enable' with the
2818+ token obtained from https://ubuntu.com/livepatch.
2819+ """
2820+ When I run `ua status` with sudo
2821+ Then stdout matches regexp:
2822+ """
2823+ esm-infra +yes +enabled +UA Infra: Extended Security Maintenance \(ESM\)
2824+ fips +yes +disabled +NIST-certified FIPS modules
2825+ fips-updates +yes +disabled +Uncertified security updates to FIPS modules
2826+ livepatch +yes +disabled +Canonical Livepatch service
2827+ """
2828+
2829+ Examples: ubuntu release
2830+ | release |
2831+ | xenial |
2832+ | bionic |
2833+
2834+ @series.bionic
2835+ @series.xenial
2836+ @uses.config.machine_type.lxd.vm
2837+ Scenario Outline: Attached enable livepatch on a machine with fips active
2838+ Given a `<release>` machine with ubuntu-advantage-tools installed
2839+ When I verify that running `canonical-livepatch status` `with sudo` exits `1`
2840 Then I will see the following on stderr:
2841 """
2842- This command must be run as root (try using sudo)
2843+ sudo: canonical-livepatch: command not found
2844+ """
2845+ When I attach `contract_token` with sudo
2846+ Then stdout matches regexp:
2847+ """
2848+ Installing canonical-livepatch snap
2849+ Canonical livepatch enabled
2850 """
2851- When I run `ua enable fips-updates` with sudo
2852+ When I run `ua status` with sudo
2853+ Then stdout matches regexp:
2854+ """
2855+ livepatch yes enabled
2856+ """
2857+ When I run `canonical-livepatch status` with sudo
2858+ Then stdout matches regexp:
2859+ """
2860+ running: true
2861+ """
2862+
2863+ Examples: ubuntu release
2864+ | release |
2865+ | xenial |
2866+ | bionic |
2867+
2868+ @series.bionic
2869+ @uses.config.machine_type.lxd.vm
2870+ Scenario: Attached enable fips on a machine with livepatch active
2871+ Given a `bionic` machine with ubuntu-advantage-tools installed
2872+ When I attach `contract_token` with sudo
2873+ Then stdout matches regexp:
2874+ """
2875+ Updating package lists
2876+ ESM Infra enabled
2877+ Installing canonical-livepatch snap
2878+ Canonical livepatch enabled
2879+ """
2880+ When I run `ua disable livepatch` with sudo
2881+ And I run `ua enable fips --assume-yes` with sudo
2882 Then I will see the following on stdout:
2883 """
2884 One moment, checking your subscription first
2885- Cannot install FIPS Updates on a container
2886+ Updating package lists
2887+ Installing FIPS packages
2888+ FIPS enabled
2889+ A reboot is required to complete install
2890+ """
2891+ When I append the following on uaclient config:
2892+ """
2893+ features:
2894+ block_disable_on_enable: true
2895+ """
2896+ Then I verify that running `ua enable livepatch` `with sudo` exits `1`
2897+ And I will see the following on stdout
2898+ """
2899+ One moment, checking your subscription first
2900+ Cannot enable Livepatch when FIPS is enabled
2901 """
2902
2903+ @series.bionic
2904+ @uses.config.machine_type.lxd.vm
2905+ Scenario: Attached enable livepatch on a machine with fips active
2906+ Given a `bionic` machine with ubuntu-advantage-tools installed
2907+ When I attach `contract_token` with sudo
2908+ Then stdout matches regexp:
2909+ """
2910+ Updating package lists
2911+ ESM Infra enabled
2912+ Installing canonical-livepatch snap
2913+ Canonical livepatch enabled
2914+ """
2915+ When I append the following on uaclient config:
2916+ """
2917+ features:
2918+ block_disable_on_enable: true
2919+ """
2920+ Then I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
2921+ And I will see the following on stdout
2922+ """
2923+ One moment, checking your subscription first
2924+ Cannot enable FIPS when Livepatch is enabled
2925+ """
2926
2927- Scenario: Attached enable of an unknown service in a trusty lxd container
2928- Given a trusty lxd container with ubuntu-advantage-tools installed
2929- When I attach contract_token with sudo
2930- And I run `ua enable foobar` as non-root
2931- Then I will see the following on stderr:
2932+ @series.xenial
2933+ @series.bionic
2934+ @uses.config.machine_type.lxd.vm
2935+ Scenario Outline: Attached enable fips on a machine with livepatch active
2936+ Given a `<release>` machine with ubuntu-advantage-tools installed
2937+ When I attach `contract_token` with sudo
2938+ Then stdout matches regexp:
2939 """
2940- This command must be run as root (try using sudo)
2941+ Updating package lists
2942+ ESM Infra enabled
2943 """
2944- When I run `ua enable foobar` with sudo
2945- Then I will see the following on stderr:
2946+ And stdout matches regexp:
2947+ """
2948+ Installing canonical-livepatch snap
2949+ Canonical livepatch enabled
2950+ """
2951+ When I run `ua enable fips --assume-yes` with sudo
2952+ Then I will see the following on stdout
2953+ """
2954+ One moment, checking your subscription first
2955+ Updating package lists
2956+ Installing FIPS packages
2957+ FIPS enabled
2958+ A reboot is required to complete install
2959 """
2960- Cannot enable 'foobar'
2961- For a list of services see: sudo ua status
2962+ When I run `ua status` with sudo
2963+ Then stdout matches regexp:
2964+ """
2965+ fips +yes +enabled
2966+ """
2967+ And stdout matches regexp:
2968+ """
2969+ livepatch +yes +n/a
2970 """
2971
2972- Scenario: Attached enable of a known service already enabled (UA Infra) in a trusty lxd container
2973- Given a trusty lxd container with ubuntu-advantage-tools installed
2974- When I attach contract_token with sudo
2975- And I run `ua enable esm-infra` as non-root
2976- Then I will see the following on stderr:
2977+ Examples: ubuntu release
2978+ | release |
2979+ | bionic |
2980+ | xenial |
2981+
2982+ @series.xenial
2983+ @series.bionic
2984+ @uses.config.machine_type.lxd.vm
2985+ Scenario Outline: Attached enable fips on a machine with fips-updates active
2986+ Given a `<release>` machine with ubuntu-advantage-tools installed
2987+ When I attach `contract_token` with sudo
2988+ Then stdout matches regexp:
2989 """
2990- This command must be run as root (try using sudo)
2991+ ESM Infra enabled
2992+ """
2993+ And stdout matches regexp:
2994+ """
2995+ Installing canonical-livepatch snap
2996+ Canonical livepatch enabled
2997 """
2998- When I run `ua enable esm-infra` with sudo
2999+ When I run `ua disable livepatch` with sudo
3000+ And I run `ua enable fips-updates --assume-yes` with sudo
3001 Then I will see the following on stdout:
3002 """
3003 One moment, checking your subscription first
3004- ESM Infra is already enabled.
3005- See: sudo ua status
3006+ Updating package lists
3007+ Installing FIPS Updates packages
3008+ FIPS Updates enabled
3009+ A reboot is required to complete install
3010 """
3011+ When I verify that running `ua enable fips --assume-yes` `with sudo` exits `1`
3012+ Then I will see the following on stdout
3013+ """
3014+ One moment, checking your subscription first
3015+ Cannot enable FIPS when FIPS Updates is enabled
3016+ """
3017+
3018+ Examples: ubuntu release
3019+ | release |
3020+ | bionic |
3021+ | xenial |
3022diff --git a/features/aws-ids.yaml b/features/aws-ids.yaml
3023new file mode 100644
3024index 0000000..e065192
3025--- /dev/null
3026+++ b/features/aws-ids.yaml
3027@@ -0,0 +1,4 @@
3028+bionic: ami-0e11aa8c6a6d58146
3029+focal: ami-0383ece2c0f6de239
3030+trusty: ami-0a64b4493bc9dc321
3031+xenial: ami-09e110a448d322f4a
3032diff --git a/features/azure-ids.yaml b/features/azure-ids.yaml
3033new file mode 100644
3034index 0000000..0072983
3035--- /dev/null
3036+++ b/features/azure-ids.yaml
3037@@ -0,0 +1,4 @@
3038+bionic: "Canonical:0001-com-ubuntu-pro-bionic:pro-18_04-lts"
3039+focal: "Canonical:0001-com-ubuntu-pro-focal:pro-20_04-lts"
3040+trusty: "Canonical:0001-com-ubuntu-pro-trusty:pro-14_04-lts"
3041+xenial: "Canonical:0001-com-ubuntu-pro-xenial:pro-16_04-lts"
3042diff --git a/features/cloud.py b/features/cloud.py
3043new file mode 100644
3044index 0000000..4443345
3045--- /dev/null
3046+++ b/features/cloud.py
3047@@ -0,0 +1,744 @@
3048+import json
3049+import os
3050+import logging
3051+import pycloudlib # type: ignore
3052+import time
3053+import yaml
3054+
3055+try:
3056+ from typing import Tuple, List, Optional # noqa
3057+except ImportError:
3058+ # typing isn't available on trusty, so ignore its absence
3059+ pass
3060+
3061+
3062+class Cloud:
3063+ """Base class for cloud providers that should be tested through behave.
3064+
3065+ :machine_type:
3066+ A string representing the type of machine to launch (pro or generic)
3067+ :region:
3068+ The region to create the cloud resources on
3069+ :param tag:
3070+ A tag to be used when creating the resources on the cloud provider
3071+ :timestamp_suffix:
3072+ Boolean set true to direct pycloudlib to append a timestamp to the end
3073+ of the provided tag.
3074+ """
3075+
3076+ name = ""
3077+ pro_ids_path = ""
3078+ env_vars: "Tuple[str, ...]" = ()
3079+
3080+ def __init__(
3081+ self,
3082+ machine_type: str,
3083+ region: "Optional[str]" = None,
3084+ tag: "Optional[str]" = None,
3085+ timestamp_suffix: bool = True,
3086+ ) -> None:
3087+ if tag:
3088+ self.tag = tag
3089+ else:
3090+ self.tag = "uaclient-ci"
3091+ self.machine_type = machine_type
3092+ self.region = region
3093+ self._api = None
3094+ self.key_name = pycloudlib.util.get_timestamped_tag(self.tag)
3095+ self.timestamp_suffix = timestamp_suffix
3096+
3097+ missing_env_vars = self.missing_env_vars()
3098+ if missing_env_vars:
3099+ logging.warning(
3100+ "".join(
3101+ [
3102+ "UACLIENT_BEHAVE_MACHINE_TYPE={} requires".format(
3103+ self.machine_type
3104+ ),
3105+ " the following env vars:\n",
3106+ *self.format_missing_env_vars(missing_env_vars),
3107+ ]
3108+ )
3109+ )
3110+
3111+ @property
3112+ def api(self) -> pycloudlib.cloud.BaseCloud:
3113+ """Return the api used to interact with the cloud provider."""
3114+ raise NotImplementedError
3115+
3116+ def _create_instance(
3117+ self,
3118+ series: str,
3119+ instance_name: "Optional[str]" = None,
3120+ image_name: "Optional[str]" = None,
3121+ user_data: "Optional[str]" = None,
3122+ ) -> pycloudlib.instance:
3123+ """Create an instance for on the cloud provider.
3124+
3125+ :param series:
3126+ The ubuntu release to be used when creating an instance. We will
3127+ create an image based on this value if the used does not provide
3128+ a image_name value
3129+ :param instance_name:
3130+ The name of the instance to be created
3131+ :param image_name:
3132+ The name of the image to be used when creating the instance
3133+ :param user_data:
3134+ The user data to be passed when creating the instance
3135+
3136+ :returns:
3137+ A cloud provider instance
3138+ """
3139+ raise NotImplementedError
3140+
3141+ def _check_cloudinit_status(
3142+ self, instance: pycloudlib.instance.BaseInstance
3143+ ) -> None:
3144+ """
3145+ Check if cloudinit was able to finish without errors.
3146+
3147+ :param instance:
3148+ An instance created on the cloud provider
3149+ """
3150+ result = instance.execute(["cloud-init", "status", "--wait", "--long"])
3151+
3152+ if result.failed:
3153+ raise OSError(
3154+ "cloud-init failed to start\n: out: %s\n error: %s"
3155+ % (result.stdout, result.stderr)
3156+ )
3157+
3158+ print("--- cloud-init succeeded")
3159+
3160+ def launch(
3161+ self,
3162+ series: str,
3163+ instance_name: "Optional[str]" = None,
3164+ image_name: "Optional[str]" = None,
3165+ user_data: "Optional[str]" = None,
3166+ ) -> pycloudlib.instance.BaseInstance:
3167+ """Create and wait for cloud provider instance to be ready.
3168+
3169+ :param series:
3170+ The ubuntu release to be used when creating an instance. We will
3171+ create an image based on this value if the used does not provide
3172+ a image_name value
3173+ :param instance_name:
3174+ The name of the instance to be created
3175+ :param image_name:
3176+ The name of the image to be used when creating the instance
3177+ :param user_data:
3178+ The user data to be passed when creating the instance
3179+
3180+ :returns:
3181+ An cloud provider instance
3182+ """
3183+ inst = self._create_instance(
3184+ series=series,
3185+ instance_name=instance_name,
3186+ image_name=image_name,
3187+ user_data=user_data,
3188+ )
3189+ print(
3190+ "--- {} instance launched: {}. Waiting for ssh access".format(
3191+ self.name, inst.name
3192+ )
3193+ )
3194+ time.sleep(15)
3195+ for sleep in (5, 10, 15):
3196+ try:
3197+ inst.wait()
3198+ break
3199+ except Exception as e:
3200+ print("--- Retrying instance.wait on {}".format(str(e)))
3201+
3202+ if series != "trusty":
3203+ self._check_cloudinit_status(inst)
3204+
3205+ return inst
3206+
3207+ def get_instance_id(
3208+ self, instance: pycloudlib.instance.BaseInstance
3209+ ) -> str:
3210+ """Return the instance identifier.
3211+
3212+ :param instance:
3213+ An instance created on the cloud provider
3214+
3215+ :returns:
3216+ The string of the unique instance id
3217+ """
3218+ return instance.id
3219+
3220+ def format_missing_env_vars(self, missing_env_vars: "List") -> "List[str]":
3221+ """Format missing env vars to be displayed in log.
3222+
3223+ :returns:
3224+ A list of env string formatted to be used when logging
3225+ """
3226+ return [" - {}\n".format(env_var) for env_var in missing_env_vars]
3227+
3228+ def missing_env_vars(self) -> "List[str]":
3229+ """Return a list of env variables necessary for this cloud provider.
3230+
3231+ :returns:
3232+ A list of string representing the missing variables
3233+ """
3234+ return [
3235+ env_name
3236+ for env_name in self.env_vars
3237+ if not getattr(
3238+ self, env_name.lower().replace("uaclient_behave_", "")
3239+ )
3240+ ]
3241+
3242+ def locate_image_name(self, series: str) -> str:
3243+ """Locate and return the image name to use for vm provision.
3244+
3245+ :param series:
3246+ The ubuntu release to be used when locating the image name
3247+
3248+ :returns:
3249+ A image name to use when provisioning a virtual machine
3250+ based on the series value
3251+ """
3252+ if not series:
3253+ raise ValueError(
3254+ "Must provide either series or image_name to launch azure"
3255+ )
3256+
3257+ if "pro" in self.machine_type:
3258+ with open(self.pro_ids_path, "r") as stream:
3259+ pro_ids = yaml.safe_load(stream.read())
3260+ image_name = pro_ids[series]
3261+ else:
3262+ image_name = self.api.daily_image(release=series)
3263+
3264+ return image_name
3265+
3266+ def manage_ssh_key(self, private_key_path: "Optional[str]" = None) -> None:
3267+ """Create and manage ssh key pairs to be used in the cloud provider.
3268+
3269+ :param private_key_path:
3270+ Location of the private key path to use. If None, the location
3271+ will be a default location.
3272+ """
3273+ cloud_name = self.name.lower().replace("_", "-")
3274+ pub_key_path = "{}-pubkey".format(cloud_name)
3275+ priv_key_path = "{}-privkey".format(cloud_name)
3276+ pub_key, priv_key = self.api.create_key_pair()
3277+
3278+ with open(pub_key_path, "w") as f:
3279+ f.write(pub_key)
3280+
3281+ with open(priv_key_path, "w") as f:
3282+ f.write(priv_key)
3283+
3284+ os.chmod(pub_key_path, 0o600)
3285+ os.chmod(priv_key_path, 0o600)
3286+
3287+ self.api.use_key(
3288+ public_key_path=pub_key_path, private_key_path=priv_key_path
3289+ )
3290+
3291+
3292+class EC2(Cloud):
3293+ """Class that represents the EC2 cloud provider.
3294+
3295+ :param aws_access_key_id:
3296+ The aws access key id
3297+ :param aws_secret_access_key:
3298+ The aws secret access key
3299+ :region:
3300+ The region to be used to create the aws instances
3301+ :machine_type:
3302+ A string representing the type of machine to launch (pro or generic)
3303+ :tag:
3304+ A tag to be used when creating the resources on the cloud provider
3305+ :timestamp_suffix:
3306+ Boolean set true to direct pycloudlib to append a timestamp to the end
3307+ of the provided tag.
3308+ """
3309+
3310+ name = "aws"
3311+ env_vars: "Tuple[str, ...]" = (
3312+ "aws_access_key_id",
3313+ "aws_secret_access_key",
3314+ )
3315+ pro_ids_path = "features/aws-ids.yaml"
3316+
3317+ def __init__(
3318+ self,
3319+ aws_access_key_id: "Optional[str]",
3320+ aws_secret_access_key: "Optional[str]",
3321+ machine_type: str,
3322+ region: "Optional[str]" = "us-east-2",
3323+ tag: "Optional[str]" = None,
3324+ timestamp_suffix: bool = True,
3325+ ) -> None:
3326+ self.aws_access_key_id = aws_access_key_id
3327+ self.aws_secret_access_key = aws_secret_access_key
3328+ logging.basicConfig(
3329+ filename="pycloudlib-behave.log", level=logging.DEBUG
3330+ )
3331+ super().__init__(
3332+ region=region,
3333+ machine_type=machine_type,
3334+ tag=tag,
3335+ timestamp_suffix=timestamp_suffix,
3336+ )
3337+
3338+ @property
3339+ def api(self) -> pycloudlib.cloud.BaseCloud:
3340+ """Return the api used to interact with the cloud provider."""
3341+ if self._api is None:
3342+ self._api = pycloudlib.EC2(
3343+ tag=self.tag,
3344+ access_key_id=self.aws_access_key_id,
3345+ secret_access_key=self.aws_secret_access_key,
3346+ region=self.region,
3347+ timestamp_suffix=self.timestamp_suffix,
3348+ )
3349+
3350+ return self._api
3351+
3352+ def manage_ssh_key(
3353+ self,
3354+ private_key_path: "Optional[str]" = None,
3355+ key_name: "Optional[str]" = None,
3356+ ) -> None:
3357+ """Create and manage ssh key pairs to be used in the cloud provider.
3358+
3359+ :param private_key_path:
3360+ Location of the private key path to use. If None, the location
3361+ will be a default location.
3362+ :param key_name:
3363+ Optional key_name to use when uploading to the cloud. Default is
3364+ uaclient-ci-<timestamp>
3365+ """
3366+ if key_name:
3367+ self.key_name = key_name
3368+ if not private_key_path:
3369+ if self.key_name in self.api.list_keys():
3370+ self.api.delete_key(self.key_name)
3371+
3372+ private_key_path = "ec2-{}.pem".format(self.key_name)
3373+ print(
3374+ "--- Creating local keyfile {} for EC2".format(
3375+ private_key_path
3376+ )
3377+ )
3378+ keypair = self.api.client.create_key_pair(KeyName=self.key_name)
3379+
3380+ with open(private_key_path, "w") as stream:
3381+ stream.write(keypair["KeyMaterial"])
3382+ os.chmod(private_key_path, 0o600)
3383+
3384+ self.api.use_key(private_key_path, private_key_path, self.key_name)
3385+
3386+ def _create_instance(
3387+ self,
3388+ series: str,
3389+ instance_name: "Optional[str]" = None,
3390+ image_name: "Optional[str]" = None,
3391+ user_data: "Optional[str]" = None,
3392+ ) -> pycloudlib.instance:
3393+ """Launch an instance on the cloud provider.
3394+
3395+ :param series:
3396+ The ubuntu release to be used when creating an instance. We will
3397+ create an image based on this value if the used does not provide
3398+ a image_name value
3399+ :param instance_name:
3400+ The name of the instance to be created
3401+ :param image_name:
3402+ The name of the image to be used when creating the instance
3403+ :param user_data:
3404+ The user data to be passed when creating the instance
3405+
3406+ :returns:
3407+ An AWS cloud provider instance
3408+ """
3409+ if not image_name:
3410+ image_name = self.locate_image_name(series)
3411+
3412+ print("--- Launching AWS image {}({})".format(image_name, series))
3413+ vpc = self.api.get_or_create_vpc(name="uaclient-integration")
3414+
3415+ try:
3416+ inst = self.api.launch(image_name, user_data=user_data, vpc=vpc)
3417+ except Exception as e:
3418+ print(str(e))
3419+ raise
3420+
3421+ return inst
3422+
3423+
3424+class Azure(Cloud):
3425+ """Class that represents the Azure cloud provider.
3426+
3427+ :param az_client_id:
3428+ The Azure client id
3429+ :param az_client_secret
3430+ The Azure client secret
3431+ :param az_tenant_id:
3432+ The Azure tenant id
3433+ :param az_subscription_id:
3434+ The Azure subscription id
3435+ :machine_type:
3436+ A string representing the type of machine to launch (pro or generic)
3437+ :region:
3438+ The region to create the resources on
3439+ :tag:
3440+ A tag to be used when creating the resources on the cloud provider
3441+ :timestamp_suffix:
3442+ Boolean set true to direct pycloudlib to append a timestamp to the end
3443+ of the provided tag.
3444+ """
3445+
3446+ name = "Azure"
3447+ env_vars: "Tuple[str, ...]" = (
3448+ "az_client_id",
3449+ "az_client_secret",
3450+ "az_tenant_id",
3451+ "az_subscription_id",
3452+ )
3453+ pro_ids_path = "features/azure-ids.yaml"
3454+
3455+ def __init__(
3456+ self,
3457+ machine_type: str,
3458+ region: "Optional[str]" = "centralus",
3459+ tag: "Optional[str]" = None,
3460+ timestamp_suffix: bool = True,
3461+ az_client_id: "Optional[str]" = None,
3462+ az_client_secret: "Optional[str]" = None,
3463+ az_tenant_id: "Optional[str]" = None,
3464+ az_subscription_id: "Optional[str]" = None,
3465+ ) -> None:
3466+ self.az_client_id = az_client_id
3467+ self.az_client_secret = az_client_secret
3468+ self.az_tenant_id = az_tenant_id
3469+ self.az_subscription_id = az_subscription_id
3470+
3471+ super().__init__(
3472+ machine_type=machine_type,
3473+ region=region,
3474+ tag=tag,
3475+ timestamp_suffix=timestamp_suffix,
3476+ )
3477+
3478+ @property
3479+ def api(self) -> pycloudlib.cloud.BaseCloud:
3480+ """Return the api used to interact with the cloud provider."""
3481+ if self._api is None:
3482+ self._api = pycloudlib.Azure(
3483+ tag=self.tag,
3484+ client_id=self.az_client_id,
3485+ client_secret=self.az_client_secret,
3486+ tenant_id=self.az_tenant_id,
3487+ subscription_id=self.az_subscription_id,
3488+ timestamp_suffix=self.timestamp_suffix,
3489+ )
3490+
3491+ return self._api
3492+
3493+ def get_instance_id(
3494+ self, instance: pycloudlib.instance.BaseInstance
3495+ ) -> str:
3496+ """Return the instance identifier.
3497+
3498+ :param instance:
3499+ An instance created on the cloud provider
3500+
3501+ :returns:
3502+ The string of the unique instance id
3503+ """
3504+ # For Azure, the API identifier uses the instance name
3505+ # instead of the instance id
3506+ return instance.name
3507+
3508+ def manage_ssh_key(self, private_key_path: "Optional[str]" = None) -> None:
3509+ """Create and manage ssh key pairs to be used in the cloud provider.
3510+
3511+ :param private_key_path:
3512+ Location of the private key path to use. If None, the location
3513+ will be a default location.
3514+ """
3515+ if not private_key_path:
3516+ private_key_path = "azure-priv-{}.pem".format(self.key_name)
3517+ pub_key_path = "azure-pub-{}.txt".format(self.key_name)
3518+ print(
3519+ "--- Creating local keyfile {} for Azure".format(
3520+ private_key_path
3521+ )
3522+ )
3523+ if not os.path.exists(private_key_path):
3524+ pub_key, priv_key = self.api.create_key_pair(
3525+ key_name=self.key_name
3526+ )
3527+
3528+ with open(pub_key_path, "w") as stream:
3529+ stream.write(pub_key)
3530+
3531+ with open(private_key_path, "w") as stream:
3532+ stream.write(priv_key)
3533+
3534+ os.chmod(pub_key_path, 0o600)
3535+ os.chmod(private_key_path, 0o600)
3536+
3537+ self.api.use_key(pub_key_path, private_key_path, self.key_name)
3538+
3539+ def _create_instance(
3540+ self,
3541+ series: str,
3542+ instance_name: "Optional[str]" = None,
3543+ image_name: "Optional[str]" = None,
3544+ user_data: "Optional[str]" = None,
3545+ ) -> pycloudlib.instance:
3546+ """Launch an instance on the cloud provider.
3547+
3548+ :param series:
3549+ The ubuntu release to be used when creating an instance. We will
3550+ create an image based on this value if the used does not provide
3551+ a image_name value
3552+ :param instance_name:
3553+ The name of the instance to be created
3554+ :param image_name:
3555+ The name of the image to be used when creating the instance
3556+ :param user_data:
3557+ The user data to be passed when creating the instance
3558+
3559+ :returns:
3560+ An AWS cloud provider instance
3561+ """
3562+ if not image_name:
3563+ image_name = self.locate_image_name(series)
3564+
3565+ print("--- Launching Azure image {}({})".format(image_name, series))
3566+ inst = self.api.launch(image_id=image_name, user_data=user_data)
3567+ return inst
3568+
3569+
3570+class GCP(Cloud):
3571+ name = "gcp"
3572+ pro_ids_path = "features/gcp-ids.yaml"
3573+
3574+ """Class that represents the Google Cloud Platform cloud provider.
3575+
3576+ :param gcp_credentials_path
3577+ The GCP credentials path to use when authentiacting to GCP
3578+ :param gcp_project
3579+ The name of the GCP project to be used
3580+ :machine_type:
3581+ A string representing the type of machine to launch (pro or generic)
3582+ :region:
3583+ The region to create the resources on
3584+ :tag:
3585+ A tag to be used when creating the resources on the cloud provider
3586+ :timestamp_suffix:
3587+ Boolean set true to direct pycloudlib to append a timestamp to the end
3588+ of the provided tag.
3589+ """
3590+
3591+ env_vars: "Tuple[str, ...]" = ("gcp_credentials_path", "gcp_project")
3592+
3593+ def __init__(
3594+ self,
3595+ machine_type: str,
3596+ region: "Optional[str]" = "us-west2",
3597+ tag: "Optional[str]" = None,
3598+ timestamp_suffix: bool = True,
3599+ zone: "Optional[str]" = "a",
3600+ gcp_credentials_path: "Optional[str]" = None,
3601+ gcp_project: "Optional[str]" = None,
3602+ ) -> None:
3603+ self.gcp_credentials_path = gcp_credentials_path
3604+ self.gcp_project = gcp_project
3605+ self.zone = zone
3606+
3607+ super().__init__(
3608+ machine_type=machine_type,
3609+ region=region,
3610+ tag=tag,
3611+ timestamp_suffix=timestamp_suffix,
3612+ )
3613+
3614+ self._set_service_account_email()
3615+
3616+ def _set_service_account_email(self):
3617+ """Set service account email if credentials provided."""
3618+ json_credentials = {}
3619+
3620+ if self.gcp_credentials_path:
3621+ with open(self.gcp_credentials_path, "r") as f:
3622+ json_credentials = json.load(f)
3623+
3624+ self.service_account_email = json_credentials.get("client_email")
3625+
3626+ @property
3627+ def api(self) -> pycloudlib.cloud.BaseCloud:
3628+ """Return the api used to interact with the cloud provider."""
3629+ if self._api is None:
3630+ self._api = pycloudlib.GCE(
3631+ tag=self.tag,
3632+ timestamp_suffix=self.timestamp_suffix,
3633+ credentials_path=self.gcp_credentials_path,
3634+ project=self.gcp_project,
3635+ zone=self.zone,
3636+ region=self.region,
3637+ service_account_email=self.service_account_email,
3638+ )
3639+
3640+ return self._api
3641+
3642+ def _create_instance(
3643+ self,
3644+ series: str,
3645+ instance_name: "Optional[str]" = None,
3646+ image_name: "Optional[str]" = None,
3647+ user_data: "Optional[str]" = None,
3648+ ) -> pycloudlib.instance:
3649+ """Launch an instance on the cloud provider.
3650+
3651+ :param series:
3652+ The ubuntu release to be used when creating an instance. We will
3653+ create an image based on this value if the used does not provide
3654+ a image_name value
3655+ :param instance_name:
3656+ The name of the instance to be created
3657+ :param image_name:
3658+ The name of the image to be used when creating the instance
3659+ :param user_data:
3660+ The user data to be passed when creating the instance
3661+
3662+ :returns:
3663+ An AWS cloud provider instance
3664+ """
3665+ if not image_name:
3666+ image_name = self.locate_image_name(series)
3667+
3668+ print("--- Launching GCP image {}({})".format(image_name, series))
3669+ inst = self.api.launch(image_id=image_name, user_data=user_data)
3670+ return inst
3671+
3672+
3673+class _LXD(Cloud):
3674+ name = "_lxd"
3675+
3676+ @property
3677+ def pycloudlib_cls(self):
3678+ """Return the pycloudlib cls to be used as an api."""
3679+ raise NotImplementedError
3680+
3681+ def _create_instance(
3682+ self,
3683+ series: str,
3684+ instance_name: "Optional[str]" = None,
3685+ image_name: "Optional[str]" = None,
3686+ user_data: "Optional[str]" = None,
3687+ ) -> pycloudlib.instance:
3688+ """Launch an instance on the cloud provider.
3689+
3690+ :param series:
3691+ The ubuntu release to be used when creating an instance. We will
3692+ create an image based on this value if the used does not provide
3693+ a image_name value
3694+ :param instance_name:
3695+ The name of the instance to be created
3696+ :param image_name:
3697+ The name of the image to be used when creating the instance
3698+ :param user_data:
3699+ The user data to be passed when creating the instance
3700+
3701+ :returns:
3702+ An AWS cloud provider instance
3703+ """
3704+ if not image_name:
3705+ image_name = self.locate_image_name(series)
3706+
3707+ image_type = self.name.title().replace("-", " ")
3708+
3709+ print(
3710+ "--- Launching {} image {}({})".format(
3711+ image_type, image_name, series
3712+ )
3713+ )
3714+
3715+ inst = self.api.launch(
3716+ name=instance_name, image_id=image_name, user_data=user_data
3717+ )
3718+ return inst
3719+
3720+ def get_instance_id(
3721+ self, instance: pycloudlib.instance.BaseInstance
3722+ ) -> str:
3723+ """Return the instance identifier.
3724+
3725+ :param instance:
3726+ An instance created on the cloud provider
3727+
3728+ :returns:
3729+ The string of the unique instance id
3730+ """
3731+ # For LXD, the API identifier uses the instance name
3732+ # instead of the instance id
3733+ return instance.name
3734+
3735+ def locate_image_name(self, series: str) -> str:
3736+ """Locate and return the image name to use for vm provision.
3737+
3738+ :param series:
3739+ The ubuntu release to be used when locating the image name
3740+
3741+ :returns:
3742+ A image name to use when provisioning a virtual machine
3743+ based on the series value
3744+ """
3745+ if not series:
3746+ raise ValueError(
3747+ "Must provide either series or image_name to launch azure"
3748+ )
3749+
3750+ image_name = self.api.daily_image(release=series)
3751+ return image_name
3752+
3753+ @property
3754+ def api(self) -> pycloudlib.cloud.BaseCloud:
3755+ """Return the api used to interact with the cloud provider."""
3756+ if self._api is None:
3757+ self._api = self.pycloudlib_cls(
3758+ tag=self.tag, timestamp_suffix=self.timestamp_suffix
3759+ )
3760+
3761+ return self._api
3762+
3763+
3764+class LXDVirtualMachine(_LXD):
3765+ name = "lxd-virtual-machine"
3766+
3767+ @property
3768+ def pycloudlib_cls(self):
3769+ """Return the pycloudlib cls to be used as an api."""
3770+ return pycloudlib.LXDVirtualMachine
3771+
3772+
3773+class LXDContainer(_LXD):
3774+ name = "lxd-container"
3775+
3776+ @property
3777+ def pycloudlib_cls(self):
3778+ """Return the pycloudlib cls to be used as an api."""
3779+ return pycloudlib.LXDContainer
3780+
3781+ def manage_ssh_key(self, private_key_path: "Optional[str]" = None) -> None:
3782+ """Create and manage ssh key pairs to be used in the cloud provider.
3783+
3784+ On a LXD container, we do not use ssh keys to communicate with the
3785+ instance. Therefore, this method should not be used.
3786+
3787+ :param private_key_path:
3788+ Location of the private key path to use. If None, the location
3789+ will be a default location.
3790+ """
3791+ pass
3792diff --git a/features/environment.py b/features/environment.py
3793index 54d00eb..9a53933 100644
3794--- a/features/environment.py
3795+++ b/features/environment.py
3796@@ -1,13 +1,70 @@
3797 import datetime
3798+import errno
3799 import os
3800-import subprocess
3801-from typing import Dict, Union
3802+import itertools
3803+import tempfile
3804+import textwrap
3805+import logging
3806+import pycloudlib # type: ignore
3807
3808-from behave.model import Scenario
3809+try:
3810+ from typing import Dict, Optional, Union, List, Tuple, Any # noqa: F401
3811+except ImportError:
3812+ # typing isn't available on trusty, so ignore its absence
3813+ pass
3814+
3815+from behave.model import Feature, Scenario
3816
3817 from behave.runner import Context
3818
3819-from features.util import launch_lxd_container, lxc_exec
3820+import features.cloud as cloud
3821+
3822+from features.util import emit_spinner_on_travis, lxc_get_property, build_debs
3823+
3824+ALL_SUPPORTED_SERIES = ["bionic", "focal", "trusty", "xenial"]
3825+
3826+DAILY_PPA = "http://ppa.launchpad.net/canonical-server/ua-client-daily/ubuntu"
3827+DAILY_PPA_KEYID = "8A295C4FB8B190B7"
3828+
3829+USERDATA_BLOCK_AUTO_ATTACH_IMG = """\
3830+#cloud-config
3831+bootcmd:
3832+ - cp /usr/bin/ua /usr/bin/ua.orig
3833+ - 'echo "#!/bin/sh\ndate >> /root/ua-calls\n" > /usr/bin/ua'
3834+ - chmod 755 /usr/bin/ua
3835+"""
3836+
3837+USERDATA_APT_SOURCE_PPA_TRUSTY = """\
3838+apt_sources: # for trusty
3839+ - source: deb {ppa_url} $RELEASE main
3840+ keyid: {ppa_keyid}
3841+packages: [{packages}]
3842+"""
3843+
3844+USERDATA_APT_SOURCE_PPA = """\
3845+apt:
3846+ sources:
3847+ ua-tools-ppa:
3848+ source: deb {ppa_url} $RELEASE main
3849+ keyid: {ppa_keyid}
3850+packages: [{packages}]
3851+"""
3852+
3853+PROCESS_LOG_TMPL = """\
3854+returncode: {}
3855+stdout:
3856+{stdout}
3857+stderr:
3858+{stderr}
3859+"""
3860+
3861+PROCESS_LOG_TMPL = """\
3862+returncode: {returncode}
3863+stdout:
3864+{stdout}
3865+stderr:
3866+{stderr}
3867+"""
3868
3869
3870 class UAClientBehaveConfig:
3871@@ -19,16 +76,32 @@ class UAClientBehaveConfig:
3872
3873 :param contract_token:
3874 A valid contract token to use during attach scenarios
3875+ :param contract_token_staging:
3876+ A valid staging contract token to use during attach scenarios
3877 :param image_clean:
3878 This indicates whether the image created for this test run should be
3879 cleaned up when all tests are complete.
3880+ :param machine_type:
3881+ The default machine_type to test: lxd.container, lxd.vm, azure.pro,
3882+ azure.generic, aws.pro or aws.generic
3883+ :param private_key_file:
3884+ Optional path to pre-existing private key file to use when connecting
3885+ launched VMs via ssh.
3886+ :param private_key_name:
3887+ Optional name of the cloud's named private key object to use when
3888+ connecting to launched VMs via ssh. Default: uaclient-integration.
3889 :param reuse_image:
3890 A string with an image name that should be used instead of building a
3891- fresh image for this test run. If specified, image_clean will be set
3892- to False.
3893+ fresh image for this test run. If specified, this image will not be
3894+ deleted.
3895 :param destroy_instances:
3896 This boolean indicates that test containers should be destroyed after
3897 the completion. Set to False to leave instances running.
3898+ :param debs_path:
3899+ Location of the debs to be used when lauching a focal integration
3900+ test. If that path is None, we will build those debs locally.
3901+ :param artifact_dir:
3902+ Location where test artifacts are emitted.
3903 """
3904
3905 prefix = "UACLIENT_BEHAVE_"
3906@@ -36,48 +109,200 @@ class UAClientBehaveConfig:
3907 # These variables are used in .from_environ() to convert the string
3908 # environment variable input to the appropriate Python types for use within
3909 # the test framework
3910- boolean_options = ["image_clean", "destroy_instances"]
3911- str_options = ["contract_token", "reuse_image"]
3912- redact_options = ["contract_token"]
3913+ boolean_options = ["build_pr", "image_clean", "destroy_instances"]
3914+ str_options = [
3915+ "aws_access_key_id",
3916+ "aws_secret_access_key",
3917+ "az_client_id",
3918+ "az_client_secret",
3919+ "az_tenant_id",
3920+ "az_subscription_id",
3921+ "gcp_credentials_path",
3922+ "gcp_project",
3923+ "contract_token",
3924+ "contract_token_staging",
3925+ "machine_type",
3926+ "private_key_file",
3927+ "private_key_name",
3928+ "reuse_image",
3929+ "debs_path",
3930+ "artifact_dir",
3931+ "ppa",
3932+ "ppa_keyid",
3933+ ]
3934+ redact_options = [
3935+ "aws_access_key_id",
3936+ "aws_secret_access_key",
3937+ "az_client_id",
3938+ "az_client_secret",
3939+ "az_tenant_id",
3940+ "az_subscription_id",
3941+ "contract_token",
3942+ "contract_token_staging",
3943+ ]
3944
3945 # This variable is used in .from_environ() but also to emit the "Config
3946 # options" stanza in __init__
3947 all_options = boolean_options + str_options
3948+ cloud_api = None # type: pycloudlib.cloud.BaseCloud
3949+ cloud_manager = None # type: cloud.Cloud
3950
3951 def __init__(
3952 self,
3953 *,
3954+ aws_access_key_id: str = None,
3955+ aws_secret_access_key: str = None,
3956+ az_client_id: str = None,
3957+ az_client_secret: str = None,
3958+ az_tenant_id: str = None,
3959+ az_subscription_id: str = None,
3960+ gcp_credentials_path: str = None,
3961+ gcp_project: str = None,
3962+ build_pr: bool = False,
3963 image_clean: bool = True,
3964 destroy_instances: bool = True,
3965+ machine_type: str = "lxd.container",
3966+ private_key_file: str = None,
3967+ private_key_name: str = "uaclient-integration",
3968 reuse_image: str = None,
3969- contract_token: str = None
3970+ contract_token: str = None,
3971+ contract_token_staging: str = None,
3972+ debs_path: str = None,
3973+ artifact_dir: str = None,
3974+ ppa: str = DAILY_PPA,
3975+ ppa_keyid: str = DAILY_PPA_KEYID,
3976+ cmdline_tags: "List" = []
3977 ) -> None:
3978 # First, store the values we've detected
3979+ self.aws_access_key_id = aws_access_key_id
3980+ self.aws_secret_access_key = aws_secret_access_key
3981+ self.az_client_id = az_client_id
3982+ self.az_client_secret = az_client_secret
3983+ self.az_tenant_id = az_tenant_id
3984+ self.az_subscription_id = az_subscription_id
3985+ self.gcp_credentials_path = gcp_credentials_path
3986+ self.gcp_project = gcp_project
3987+ self.build_pr = build_pr
3988 self.contract_token = contract_token
3989+ self.contract_token_staging = contract_token_staging
3990 self.image_clean = image_clean
3991 self.destroy_instances = destroy_instances
3992+ self.machine_type = machine_type
3993+ self.private_key_file = private_key_file
3994+ self.private_key_name = private_key_name
3995 self.reuse_image = reuse_image
3996-
3997+ self.cmdline_tags = cmdline_tags
3998+ self.debs_path = debs_path
3999+ self.artifact_dir = artifact_dir
4000+ self.ppa = ppa
4001+ self.ppa_keyid = ppa_keyid
4002+ self.filter_series = set(
4003+ [
4004+ tag.split(".")[1]
4005+ for tag in cmdline_tags
4006+ if tag.startswith("series.") and "series.all" not in tag
4007+ ]
4008+ )
4009 # Next, perform any required validation
4010 if self.reuse_image is not None:
4011 if self.image_clean:
4012- print("reuse_image specified, setting image_clean = False")
4013- self.image_clean = False
4014+ print(" Reuse_image specified, it will not be deleted.")
4015+
4016+ ignore_vars = () # type: Tuple[str, ...]
4017+ if "aws" not in self.machine_type:
4018+ ignore_vars += cloud.EC2.env_vars
4019+ if "azure" not in self.machine_type:
4020+ ignore_vars += cloud.Azure.env_vars
4021+ if "gcp" not in self.machine_type:
4022+ ignore_vars += cloud.GCP.env_vars
4023+ if "pro" in self.machine_type:
4024+ ignore_vars += (
4025+ "UACLIENT_BEHAVE_CONTRACT_TOKEN",
4026+ "UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING",
4027+ )
4028+ for env_name in ignore_vars:
4029+ attr_name = env_name.replace("UACLIENT_BEHAVE_", "").lower()
4030+ if getattr(self, attr_name):
4031+ print(
4032+ " --- Ignoring {} because machine_type is {}".format(
4033+ env_name, self.machine_type
4034+ )
4035+ )
4036+ setattr(self, attr_name, None)
4037+ timed_job_tag = datetime.datetime.utcnow().strftime(
4038+ "uaclient-ci-%m%d-%H%M-"
4039+ )
4040+ # Jenkinsfile provides us with UACLIENT_BEHAVE_JENKINS_BUILD_TAG
4041+ job_suffix = os.environ.get("UACLIENT_BEHAVE_JENKINS_BUILD_TAG")
4042+ print("--- job suffix: {}".format(job_suffix), flush=True)
4043+ if not job_suffix:
4044+ job_suffix = os.environ.get("CHANGE_ID", "dev")
4045+ else:
4046+ job_suffix = job_suffix.split("PR-")[-1]
4047+ timed_job_tag += str(job_suffix)
4048+ timed_job_tag = timed_job_tag.replace(".", "-")
4049+ if "aws" in self.machine_type:
4050+ self.cloud_manager = cloud.EC2(
4051+ aws_access_key_id,
4052+ aws_secret_access_key,
4053+ region=os.environ.get("AWS_DEFAULT_REGION", "us-east-2"),
4054+ machine_type=self.machine_type,
4055+ tag=timed_job_tag,
4056+ timestamp_suffix=False,
4057+ )
4058+ elif "azure" in self.machine_type:
4059+ self.cloud_manager = cloud.Azure(
4060+ az_client_id=az_client_id,
4061+ az_client_secret=az_client_secret,
4062+ az_tenant_id=az_tenant_id,
4063+ az_subscription_id=az_subscription_id,
4064+ machine_type=self.machine_type,
4065+ tag=timed_job_tag,
4066+ timestamp_suffix=False,
4067+ )
4068+ elif "gcp" in self.machine_type:
4069+ self.cloud_manager = cloud.GCP(
4070+ machine_type=self.machine_type,
4071+ tag=timed_job_tag,
4072+ timestamp_suffix=False,
4073+ gcp_credentials_path=self.gcp_credentials_path,
4074+ gcp_project=gcp_project,
4075+ )
4076+ elif "lxd.vm" in self.machine_type:
4077+ self.cloud_manager = cloud.LXDVirtualMachine(
4078+ machine_type=self.machine_type
4079+ )
4080+ else:
4081+ self.cloud_manager = cloud.LXDContainer(
4082+ machine_type=self.machine_type
4083+ )
4084+
4085+ self.cloud_api = self.cloud_manager.api
4086
4087 # Finally, print the config options. This helps users debug the use of
4088 # config options, and means they'll be included in test logs in CI.
4089 print("Config options:")
4090 for option in self.all_options:
4091- value = getattr(self, option, "ERROR")
4092- if option in self.redact_options and value not in (None, "ERROR"):
4093+ value = getattr(self, option, "<UNSET>")
4094+ if option in self.redact_options and value not in (
4095+ None,
4096+ "<UNSET>",
4097+ ):
4098 value = "<REDACTED>"
4099 print(" {}".format(option), "=", value)
4100
4101 @classmethod
4102- def from_environ(cls) -> "UAClientBehaveConfig":
4103+ def from_environ(cls, config) -> "UAClientBehaveConfig":
4104 """Gather config options from os.environ and return a config object"""
4105 # First, gather all known options
4106- kwargs: Dict[str, Union[str, bool]] = {}
4107+ kwargs: Dict[str, Union[str, bool, "List"]] = {}
4108+ # Preserve cmdline_tags for reference
4109+ if not config.tags.ands:
4110+ kwargs["cmdline_tags"] = []
4111+ else:
4112+ kwargs["cmdline_tags"] = list(
4113+ itertools.chain.from_iterable(config.tags.ands)
4114+ )
4115 for key, value in os.environ.items():
4116 if not key.startswith(cls.prefix):
4117 continue
4118@@ -98,96 +323,440 @@ class UAClientBehaveConfig:
4119
4120
4121 def before_all(context: Context) -> None:
4122- """behave will invoke this before anything else happens.
4123-
4124- In this function, we launch a container, install ubuntu-advantage-tools and
4125- then capture an image. This image is then reused by each feature, reducing
4126- test execution time.
4127- """
4128+ """behave will invoke this before anything else happens."""
4129+ context.config.setup_logging()
4130 userdata = context.config.userdata
4131- context.reuse_container = userdata.get("reuse_container")
4132- context.config = UAClientBehaveConfig.from_environ()
4133- if context.config.reuse_image is None:
4134- create_trusty_uat_lxd_image(context)
4135- else:
4136- context.image_name = context.config.reuse_image
4137-
4138-
4139-def before_scenario(context: Context, scenario: Scenario):
4140- for tag in scenario.effective_tags:
4141+ if userdata:
4142+ logging.debug("Userdata key / value pairs:")
4143+ print("Userdata key / value pairs:")
4144+ for key, value in userdata.items():
4145+ logging.debug(" - {} = {}".format(key, value))
4146+ print(" - {} = {}".format(key, value))
4147+ context.series_image_name = {}
4148+ context.series_reuse_image = ""
4149+ context.reuse_container = {}
4150+ context.config = UAClientBehaveConfig.from_environ(context.config)
4151+ context.config.cloud_manager.manage_ssh_key()
4152+
4153+ if context.config.reuse_image:
4154+ series = lxc_get_property(
4155+ context.config.reuse_image, property_name="series", image=True
4156+ )
4157+ machine_type = lxc_get_property(
4158+ context.config.reuse_image,
4159+ property_name="machine_type",
4160+ image=True,
4161+ )
4162+ if machine_type:
4163+ print("Found machine_type: {vm_type}".format(vm_type=machine_type))
4164+ if series is not None:
4165+ context.series_reuse_image = series
4166+ context.series_image_name[series] = context.config.reuse_image
4167+ else:
4168+ print(" Could not check image series. It will not be used. ")
4169+ context.config.reuse_image = None
4170+
4171+ if userdata.get("reuse_container"):
4172+ inst = context.config.cloud_api.get_instance(
4173+ userdata.get("reuse_container")
4174+ )
4175+ codename = inst.execute(
4176+ ["grep", "UBUNTU_CODENAME", "/etc/os-release"]
4177+ ).strip()
4178+ [_, series] = codename.split("=")
4179+
4180+ context.reuse_container = {series: userdata.get("reuse_container")}
4181+ print(
4182+ textwrap.dedent(
4183+ """
4184+ You are providing a {series} container. Make sure you are running
4185+ this series tests. For instance: --tags=series.{series}""".format(
4186+ series=series
4187+ )
4188+ )
4189+ )
4190+
4191+
4192+def _should_skip_tags(context: Context, tags: "List") -> str:
4193+ """Return a reason if a feature or scenario should be skipped"""
4194+ machine_type = getattr(context.config, "machine_type", "")
4195+ machine_types = []
4196+
4197+ for tag in tags:
4198 parts = tag.split(".")
4199- if parts[0] == "uses":
4200- val = context
4201- for attr in parts[1:]:
4202- val = getattr(val, attr, None)
4203- if val is None:
4204- scenario.skip(
4205- reason="Skipped because tag value was None: {}".format(
4206- tag
4207+ if parts[0] != "uses":
4208+ continue # Only process @uses.* tags for skipping:
4209+ val = context
4210+ for idx, attr in enumerate(parts[1:], 1):
4211+ val = getattr(val, attr, None)
4212+ if attr == "machine_type":
4213+ curr_machine_type = ".".join(parts[idx + 1 :])
4214+ machine_types.append(curr_machine_type)
4215+ if curr_machine_type == machine_type:
4216+ if machine_type.startswith("lxd"):
4217+ return ""
4218+
4219+ cloud_manager = context.config.cloud_manager
4220+ if cloud_manager and cloud_manager.missing_env_vars():
4221+ return "".join(
4222+ (
4223+ "Skipped: {} machine_type requires:\n".format(
4224+ machine_type
4225+ ),
4226+ *cloud_manager.format_missing_env_vars(
4227+ cloud_manager.missing_env_vars()
4228+ ),
4229+ )
4230 )
4231- )
4232+ return ""
4233+ break
4234+ if val is None:
4235+ return "Skipped: tag value was None: {}".format(tag)
4236+
4237+ if machine_types:
4238+ return "Skipped: machine type {} was not found in tags:\n {}".format(
4239+ machine_type, ", ".join(machine_types)
4240+ )
4241+
4242+ return ""
4243+
4244
4245+def before_feature(context: Context, feature: Feature):
4246+ reason = _should_skip_tags(context, feature.tags)
4247+ if reason:
4248+ feature.skip(reason=reason)
4249
4250-def _capture_container_as_image(container_name: str, image_name: str) -> None:
4251- """Capture a lxd container as an image.
4252+
4253+def before_scenario(context: Context, scenario: Scenario):
4254+ """
4255+ In this function, we launch a container, install ubuntu-advantage-tools and
4256+ then capture an image. This image is then reused by each scenario, reducing
4257+ test execution time.
4258+ """
4259+ reason = _should_skip_tags(context, scenario.effective_tags)
4260+ if reason:
4261+ scenario.skip(reason=reason)
4262+
4263+
4264+FAILURE_FILES = (
4265+ "/etc/ubuntu-advantage/uaclient.log",
4266+ "/var/log/cloud-init.log",
4267+ "/var/log/ubuntu-advantage.log",
4268+ "/var/lib/cloud/instance/user-data.txt",
4269+ "/var/lib/cloud/instance/vendor-data.txt",
4270+)
4271+FAILURE_CMDS = {
4272+ "ua-version": ["ua", "version"],
4273+ "cloud-init-analyze": ["cloud-init", "analyze", "show"],
4274+ "cloud-init.status": ["cloud-init", "status", "--long"],
4275+ "status.json": ["ua", "status", "--all", "--format=json"],
4276+ "journal.log": ["journalctl", "-b", "0"],
4277+ "systemd-analyze-blame": ["systemd-analyze", "blame"],
4278+ "systemctl-status": ["systemctl", "status"],
4279+ "systemctl-status-ua-auto-attach": [
4280+ "systemctl",
4281+ "status",
4282+ "ua-auto-attach.service",
4283+ ],
4284+ "systemctl-status-ua-reboot-cmds": [
4285+ "systemctl",
4286+ "status",
4287+ "ua-reboot-cmds.service",
4288+ ],
4289+}
4290+
4291+
4292+def after_step(context, step):
4293+ """Collect test artifacts in the event of failure."""
4294+ if step.status == "failed":
4295+ if context.config.artifact_dir:
4296+ artifacts_dir = context.config.artifact_dir
4297+ else:
4298+ artifacts_dir = "artifacts"
4299+ artifacts_dir = os.path.join(
4300+ artifacts_dir,
4301+ "{}_{}".format(os.path.basename(step.filename), step.line),
4302+ )
4303+ if hasattr(context, "process"):
4304+ if not os.path.exists(artifacts_dir):
4305+ os.makedirs(artifacts_dir)
4306+ artifact_file = os.path.join(artifacts_dir, "process.log")
4307+ process = context.process
4308+ with open(artifact_file, "w") as stream:
4309+ stream.write(
4310+ PROCESS_LOG_TMPL.format(
4311+ returncode=process.returncode,
4312+ stdout=process.stdout,
4313+ stderr=process.stderr,
4314+ )
4315+ )
4316+
4317+ if hasattr(context, "instance"):
4318+ if not os.path.exists(artifacts_dir):
4319+ os.makedirs(artifacts_dir)
4320+ for log_file in FAILURE_FILES:
4321+ artifact_file = os.path.join(
4322+ artifacts_dir, os.path.basename(log_file)
4323+ )
4324+ print("-- pull instance:{} {}".format(log_file, artifact_file))
4325+ try:
4326+ context.instance.pull_file(log_file, artifact_file)
4327+ except IOError as e:
4328+ if e.errno == errno.EACCES:
4329+ result = context.instance.execute(
4330+ ["cat", log_file], use_sudo=True
4331+ )
4332+ with open(artifact_file, "w") as stream:
4333+ stream.write(result.stdout)
4334+ except RuntimeError:
4335+ # File did not exist
4336+ with open(artifact_file, "w") as stream:
4337+ stream.write("")
4338+ for artifact_file, cmd in FAILURE_CMDS.items():
4339+ result = context.instance.execute(cmd, use_sudo=True)
4340+ artifact_file = os.path.join(artifacts_dir, artifact_file)
4341+ with open(artifact_file, "w") as stream:
4342+ stream.write(result.stdout)
4343+
4344+
4345+def after_all(context):
4346+ if context.config.image_clean:
4347+ for key, image in context.series_image_name.items():
4348+ if key == context.series_reuse_image:
4349+ print(
4350+ " Not deleting this image: ",
4351+ context.series_image_name[key],
4352+ )
4353+ else:
4354+ context.config.cloud_api.delete_image(image)
4355+
4356+
4357+def _capture_container_as_image(
4358+ container_name: str,
4359+ image_name: str,
4360+ cloud_api: "pycloudlib.cloud.BaseCloud",
4361+) -> str:
4362+ """Capture a container as an image.
4363
4364 :param container_name:
4365 The name of the container to be captured. Note that this container
4366 will be stopped.
4367 :param image_name:
4368 The name under which the image should be published.
4369+ :param cloud_api: Optional pycloud BaseCloud api for applicable
4370+ machine_types.
4371+ """
4372+ print(
4373+ "--- Creating base image snapshot from vm {}".format(container_name)
4374+ )
4375+ inst = cloud_api.get_instance(container_name)
4376+ return cloud_api.snapshot(instance=inst)
4377+
4378+
4379+def build_debs_from_dev_instance(context: Context, series: str) -> "List[str]":
4380+ """Create a development instance, instal build dependencies and build debs
4381+
4382+
4383+ Will stop the development instance after deb build succeeds.
4384+
4385+ :return: A list of paths to applicable deb files published.
4386 """
4387- subprocess.run(["lxc", "stop", container_name])
4388- subprocess.run(["lxc", "publish", container_name, "--alias", image_name])
4389+ time_suffix = datetime.datetime.now().strftime("%s%f")
4390+ deb_paths = []
4391+
4392+ if context.config.debs_path:
4393+ print(
4394+ "--- Checking if debs can be reused in {}".format(
4395+ context.config.debs_path
4396+ )
4397+ )
4398+ debs_path = context.config.debs_path
4399+ if os.path.isdir(debs_path):
4400+ deb_paths = [
4401+ os.path.join(debs_path, deb_file)
4402+ for deb_file in os.listdir(debs_path)
4403+ if series in deb_file
4404+ ]
4405+
4406+ if len(deb_paths):
4407+ print("--- Reusing debs: {}".format(", ".join(deb_paths)))
4408+ else:
4409+ print("--- Could not find any debs to reuse. Building it locally")
4410+ print(
4411+ "--- Launching vm to build ubuntu-advantage*debs from local source"
4412+ )
4413+ build_container_name = (
4414+ "ubuntu-behave-image-pre-build-%s-" % series + time_suffix
4415+ )
4416+
4417+ cloud_manager = context.config.cloud_manager
4418+ if "pro" in context.config.machine_type:
4419+ user_data = USERDATA_BLOCK_AUTO_ATTACH_IMG
4420+ else:
4421+ user_data = ""
4422+ inst = cloud_manager.launch(
4423+ instance_name=build_container_name,
4424+ series=series,
4425+ user_data=user_data,
4426+ )
4427+
4428+ build_container_name = cloud_manager.get_instance_id(inst)
4429
4430+ with emit_spinner_on_travis("Building debs from local source... "):
4431+ deb_paths = build_debs(
4432+ build_container_name,
4433+ output_deb_dir=os.path.join(tempfile.gettempdir(), series),
4434+ cloud_api=context.config.cloud_api,
4435+ )
4436
4437-def create_trusty_uat_lxd_image(context: Context) -> None:
4438- """Create a trusty lxd image with ubuntu-advantage-tools installed
4439+ if "pro" in context.config.machine_type:
4440+ return deb_paths
4441+ # Redact ubuntu-advantage-pro deb as inapplicable
4442+ return [deb_path for deb_path in deb_paths if "pro" not in deb_path]
4443+
4444+
4445+def create_uat_image(context: Context, series: str) -> None:
4446+ """Create a given series lxd image with ubuntu-advantage-tools installed
4447
4448 This will launch a container, install ubuntu-advantage-tools, and publish
4449- the image. The image's name is stored in context.image_name for use within
4450- step code.
4451+ the image. The image's name is stored in context.series_image_name for
4452+ use within step code.
4453
4454 :param context:
4455- A `behave.runner.Context`; this will have `image_name` set on it.
4456+ A `behave.runner.Context`; this will have `series.image_name` set on
4457+ it.
4458+ :param series:
4459+ A string representing the series name to create
4460 """
4461
4462- def image_cleanup() -> None:
4463- if context.config.image_clean:
4464- subprocess.run(["lxc", "image", "delete", context.image_name])
4465+ if series in context.reuse_container:
4466+ print(
4467+ "\n Reusing the existing container: ",
4468+ context.reuse_container[series],
4469+ )
4470+ return
4471+ time_suffix = datetime.datetime.now().strftime("%s%f")
4472+ deb_paths = []
4473+ if context.config.build_pr:
4474+ deb_paths = build_debs_from_dev_instance(context, series)
4475+
4476+ print(
4477+ "--- Launching VM to create a base image with updated ubuntu-advantage"
4478+ )
4479+
4480+ is_vm = bool(context.config.machine_type == "lxd.vm")
4481+ build_container_name = "ubuntu-behave-image-build-%s-%s" % (
4482+ "-vm" if is_vm else "",
4483+ series + time_suffix,
4484+ )
4485+
4486+ user_data = ""
4487+ if "pro" in context.config.machine_type:
4488+ user_data = USERDATA_BLOCK_AUTO_ATTACH_IMG
4489+ if not deb_paths:
4490+ if not user_data:
4491+ user_data = "#cloud-config\n"
4492+ ppa = context.config.ppa
4493+ ppa_keyid = context.config.ppa_keyid
4494+ if context.config.ppa.startswith("ppa:"):
4495+ ppa = ppa.replace("ppa:", "http://ppa.launchpad.net/") + "/ubuntu"
4496+ if series == "trusty":
4497+ packages = ["ubuntu-advantage-tools"]
4498+
4499+ if "pro" in context.config.machine_type:
4500+ packages.append("ubuntu-advantage-pro")
4501+
4502+ user_data += USERDATA_APT_SOURCE_PPA_TRUSTY.format(
4503+ ppa_url=ppa, ppa_keyid=ppa_keyid, packages=", ".join(packages)
4504+ )
4505 else:
4506- print("Image cleanup disabled, not deleting:", context.image_name)
4507+ packages = ["openssh-server", "ubuntu-advantage-tools"]
4508
4509- if context.reuse_container:
4510- print(" Reusing the existent container: ", context.reuse_container)
4511- else:
4512- now = datetime.datetime.now()
4513- context.image_name = "behave-image-" + now.strftime("%s%f")
4514- build_container_name = "behave-image-build-" + now.strftime("%s%f")
4515- launch_lxd_container(context, "ubuntu:trusty", build_container_name)
4516- _install_uat_in_container(build_container_name)
4517- _capture_container_as_image(build_container_name, context.image_name)
4518- context.add_cleanup(image_cleanup)
4519+ if "pro" in context.config.machine_type:
4520+ packages.append("ubuntu-advantage-pro")
4521
4522+ user_data += USERDATA_APT_SOURCE_PPA.format(
4523+ ppa_url=ppa, ppa_keyid=ppa_keyid, packages=", ".join(packages)
4524+ )
4525+ inst = context.config.cloud_manager.launch(
4526+ instance_name=build_container_name, series=series, user_data=user_data
4527+ )
4528+ build_container_name = context.config.cloud_manager.get_instance_id(inst)
4529
4530-def _install_uat_in_container(container_name: str) -> None:
4531+ _install_uat_in_container(
4532+ build_container_name,
4533+ series=series,
4534+ config=context.config,
4535+ deb_paths=deb_paths,
4536+ )
4537+
4538+ image_name = _capture_container_as_image(
4539+ build_container_name,
4540+ image_name="ubuntu-behave-image-%s-" % series + time_suffix,
4541+ cloud_api=context.config.cloud_api,
4542+ )
4543+ context.series_image_name[series] = image_name
4544+ inst.delete(wait=False)
4545+
4546+
4547+def _install_uat_in_container(
4548+ container_name: str,
4549+ series: str,
4550+ config: UAClientBehaveConfig,
4551+ deb_paths: "Optional[List[str]]" = None,
4552+) -> None:
4553 """Install ubuntu-advantage-tools into the specified container
4554
4555 :param container_name:
4556 The name of the container into which ubuntu-advantage-tools should be
4557 installed.
4558+ :param series: The name of the series that is being used
4559+ :param config: UAClientBehaveConfig
4560+ :param deb_paths: Optional paths to local deb files we need to install
4561 """
4562- lxc_exec(
4563- container_name,
4564- [
4565- "sudo",
4566- "add-apt-repository",
4567- "--yes",
4568- "ppa:canonical-server/ua-client-daily",
4569- ],
4570- )
4571- lxc_exec(container_name, ["sudo", "apt-get", "update", "-qq"])
4572- lxc_exec(
4573- container_name,
4574- ["sudo", "apt-get", "install", "-qq", "-y", "ubuntu-advantage-tools"],
4575- )
4576+ cmds: "List[Any]" = [["systemctl", "is-system-running", "--wait"]]
4577+
4578+ if deb_paths is None:
4579+ deb_paths = []
4580+
4581+ if deb_paths:
4582+ cmds.append(["sudo", "apt-get", "update", "-qqy"])
4583+
4584+ deb_files = []
4585+ inst = config.cloud_api.get_instance(container_name)
4586+
4587+ for deb_file in deb_paths:
4588+ if "pro" in deb_file and "pro" not in config.machine_type:
4589+ continue
4590+
4591+ deb_name = os.path.basename(deb_file)
4592+ deb_files.append("/tmp/" + deb_name)
4593+ inst.push_file(deb_file, "/tmp/" + deb_name)
4594+
4595+ if series == "trusty":
4596+ cmds.append(["sudo", "dpkg", "-i"] + deb_files)
4597+ else:
4598+ cmds.append(["sudo", "apt-get", "install", "-y"] + deb_files)
4599+
4600+ if "pro" in config.machine_type:
4601+ features = "features:\n disable_auto_attach: true\n"
4602+ conf_path = "/etc/ubuntu-advantage/uaclient.conf"
4603+ cmd = "printf '{}' > /tmp/uaclient.conf".format(features)
4604+ cmds.append('sh -c "{}"'.format(cmd))
4605+ cmds.append(
4606+ 'sudo -- sh -c "cat /tmp/uaclient.conf >> {}"'.format(conf_path)
4607+ )
4608+ cmds.append("sudo ua detach --assume-yes")
4609+
4610+ cmds.append(["ua", "version"])
4611+ instance = config.cloud_api.get_instance(container_name)
4612+ for cmd in cmds: # type: ignore
4613+ result = instance.execute(cmd)
4614+ if result.failed:
4615+ print(
4616+ "--- Failed {}: out {} err {}".format(
4617+ cmd, result.stdout, result.stderr
4618+ )
4619+ )
4620+ elif "version" in cmd:
4621+ print("--- " + result)
4622diff --git a/features/gcp-ids.yaml b/features/gcp-ids.yaml
4623new file mode 100644
4624index 0000000..a3ca5bd
4625--- /dev/null
4626+++ b/features/gcp-ids.yaml
4627@@ -0,0 +1,3 @@
4628+xenial: "projects/ubuntu-os-cloud-image-proposed/global/images/testing-ubuntu-pro-1604-xenial-v20210205"
4629+bionic: "projects/ubuntu-os-cloud-image-proposed/global/images/testing-ubuntu-pro-1804-bionic-v20210205"
4630+focal: "projects/ubuntu-os-cloud-image-proposed/global/images/testing-ubuntu-pro-2004-focal-v20210205"
4631diff --git a/features/staging_commands.feature b/features/staging_commands.feature
4632new file mode 100644
4633index 0000000..90c212f
4634--- /dev/null
4635+++ b/features/staging_commands.feature
4636@@ -0,0 +1,395 @@
4637+@uses.config.contract_token_staging
4638+Feature: Enable command behaviour when attached to an UA staging subscription
4639+
4640+ @series.xenial
4641+ Scenario: Attached enable CC EAL service in a xenial lxd container
4642+ Given a `xenial` machine with ubuntu-advantage-tools installed
4643+ When I attach `contract_token_staging` with sudo
4644+ Then I verify that running `ua enable cc-eal` `as non-root` exits `1`
4645+ And I will see the following on stderr:
4646+ """
4647+ This command must be run as root (try using sudo)
4648+ """
4649+ When I run `ua enable cc-eal --beta` with sudo
4650+ Then I will see the following on stdout:
4651+ """
4652+ One moment, checking your subscription first
4653+ GPG key '/usr/share/keyrings/ubuntu-cc-keyring.gpg' not found
4654+ """
4655+ @series.xenial
4656+ @series.bionic
4657+ @series.focal
4658+ Scenario Outline: Attached enable esm-apps on a machine
4659+ Given a `<release>` machine with ubuntu-advantage-tools installed
4660+ When I attach `contract_token_staging` with sudo
4661+ And I run `ua status --all` as non-root
4662+ Then stdout matches regexp
4663+ """
4664+ esm-apps yes enabled UA Apps: Extended Security Maintenance \(ESM\)
4665+ """
4666+ And I verify that running `apt update` `with sudo` exits `0`
4667+ When I run `apt-cache policy` as non-root
4668+ Then apt-cache policy for the following url has permission `500`
4669+ """
4670+ https://esm.staging.ubuntu.com/apps/ubuntu <release>-apps-updates/main amd64 Packages
4671+ """
4672+ And apt-cache policy for the following url has permission `500`
4673+ """
4674+ https://esm.staging.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
4675+ """
4676+ And I verify that running `apt update` `with sudo` exits `0`
4677+ When I run `apt install -y <apps-pkg>` with sudo, retrying exit [100]
4678+ And I run `apt-cache policy <apps-pkg>` as non-root
4679+ Then stdout matches regexp:
4680+ """
4681+ Version table:
4682+ \s*\*\*\* .* 500
4683+ \s*500 https://esm.staging.ubuntu.com/apps/ubuntu <release>-apps-security/main amd64 Packages
4684+ """
4685+
4686+ Examples: ubuntu release
4687+ | release | apps-pkg |
4688+ | bionic | bundler |
4689+ | focal | ant |
4690+ | trusty | ant |
4691+ | xenial | jq |
4692+
4693+ @series.xenial
4694+ @series.bionic
4695+ @uses.config.machine_type.lxd.vm
4696+ Scenario Outline: Attached enable of vm-based services in an ubuntu lxd vm
4697+ Given a `<release>` machine with ubuntu-advantage-tools installed
4698+ When I attach `contract_token_staging` with sudo
4699+ And I run `ua disable livepatch` with sudo
4700+ And I run `apt-get install openssh-client openssh-server strongswan -y` with sudo, retrying exit [100]
4701+ And I run `apt-mark hold openssh-client openssh-server strongswan` with sudo
4702+ And I run `ua enable <fips-service> --assume-yes` with sudo
4703+ Then stdout matches regexp:
4704+ """
4705+ Updating package lists
4706+ Installing <fips-name> packages
4707+ <fips-name> enabled
4708+ A reboot is required to complete install
4709+ """
4710+ When I run `ua status --all` with sudo
4711+ Then stdout matches regexp:
4712+ """
4713+ <fips-service> +yes enabled
4714+ """
4715+ And stdout matches regexp:
4716+ """
4717+ FIPS support requires system reboot to complete configuration
4718+ """
4719+ And I verify that running `apt update` `with sudo` exits `0`
4720+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
4721+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
4722+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
4723+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
4724+ And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
4725+ And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
4726+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
4727+ When I reboot the `<release>` machine
4728+ And I run `uname -r` as non-root
4729+ Then stdout matches regexp:
4730+ """
4731+ fips
4732+ """
4733+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
4734+ Then I will see the following on stdout:
4735+ """
4736+ 1
4737+ """
4738+ When I run `ua status --all` with sudo
4739+ Then stdout does not match regexp:
4740+ """
4741+ FIPS support requires system reboot to complete configuration
4742+ """
4743+ When I run `ua disable <fips-service> --assume-yes` with sudo
4744+ Then stdout matches regexp:
4745+ """
4746+ Updating package lists
4747+ A reboot is required to complete disable operation
4748+ """
4749+ When I run `ua status --all` with sudo
4750+ Then stdout matches regexp:
4751+ """
4752+ Disabling FIPS requires system reboot to complete operation
4753+ """
4754+ When I run `apt-cache policy ubuntu-fips` as non-root
4755+ Then stdout matches regexp:
4756+ """
4757+ .*Installed: \(none\)
4758+ """
4759+ When I reboot the `<release>` machine
4760+ Then I verify that `openssh-server` installed version matches regexp `fips`
4761+ And I verify that `openssh-client` installed version matches regexp `fips`
4762+ And I verify that `strongswan` installed version matches regexp `fips`
4763+ And I verify that `openssh-server-hmac` installed version matches regexp `fips`
4764+ And I verify that `openssh-client-hmac` installed version matches regexp `fips`
4765+ And I verify that `strongswan-hmac` installed version matches regexp `fips`
4766+ When I run `apt-mark unhold openssh-client openssh-server strongswan` with sudo
4767+ Then I will see the following on stdout:
4768+ """
4769+ openssh-client was already not hold.
4770+ openssh-server was already not hold.
4771+ strongswan was already not hold.
4772+ """
4773+ When I run `ua status --all` with sudo
4774+ Then stdout matches regexp:
4775+ """
4776+ <fips-service> +yes disabled
4777+ """
4778+ Then stdout does not match regexp:
4779+ """
4780+ Disabling FIPS requires system reboot to complete operation
4781+ """
4782+
4783+ Examples: ubuntu release
4784+ | release | fips-name | fips-service |fips-apt-source |
4785+ | xenial | FIPS | fips |https://esm.staging.ubuntu.com/fips/ubuntu xenial/main |
4786+ | bionic | FIPS | fips |https://esm.staging.ubuntu.com/fips/ubuntu bionic/main |
4787+
4788+ @series.xenial
4789+ @series.bionic
4790+ @uses.config.machine_type.lxd.vm
4791+ Scenario Outline: Attached enable of vm-based services in an ubuntu lxd vm
4792+ Given a `<release>` machine with ubuntu-advantage-tools installed
4793+ When I attach `contract_token_staging` with sudo
4794+ And I run `ua disable livepatch` with sudo
4795+ And I run `apt-get install openssh-client openssh-server strongswan -y` with sudo, retrying exit [100]
4796+ When I run `ua enable <fips-service> --assume-yes` with sudo
4797+ Then stdout matches regexp:
4798+ """
4799+ Updating package lists
4800+ Installing <fips-name> packages
4801+ <fips-name> enabled
4802+ A reboot is required to complete install
4803+ """
4804+ When I run `ua status --all` with sudo
4805+ Then stdout matches regexp:
4806+ """
4807+ <fips-service> +yes enabled
4808+ """
4809+ And I verify that running `apt update` `with sudo` exits `0`
4810+ And I verify that running `grep Traceback /var/log/ubuntu-advantage.log` `with sudo` exits `1`
4811+ And I verify that `openssh-server` is installed from apt source `<fips-apt-source>`
4812+ And I verify that `openssh-client` is installed from apt source `<fips-apt-source>`
4813+ And I verify that `strongswan` is installed from apt source `<fips-apt-source>`
4814+ And I verify that `openssh-server-hmac` is installed from apt source `<fips-apt-source>`
4815+ And I verify that `openssh-client-hmac` is installed from apt source `<fips-apt-source>`
4816+ And I verify that `strongswan-hmac` is installed from apt source `<fips-apt-source>`
4817+ When I reboot the `<release>` machine
4818+ And I run `uname -r` as non-root
4819+ Then stdout matches regexp:
4820+ """
4821+ fips
4822+ """
4823+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
4824+ Then I will see the following on stdout:
4825+ """
4826+ 1
4827+ """
4828+ When I run `ua disable <fips-service> --assume-yes` with sudo
4829+ Then stdout matches regexp:
4830+ """
4831+ Updating package lists
4832+ A reboot is required to complete disable operation
4833+ """
4834+ When I reboot the `<release>` machine
4835+ Then I verify that `openssh-server` installed version matches regexp `fips`
4836+ And I verify that `openssh-client` installed version matches regexp `fips`
4837+ And I verify that `strongswan` installed version matches regexp `fips`
4838+ And I verify that `openssh-server-hmac` installed version matches regexp `fips`
4839+ And I verify that `openssh-client-hmac` installed version matches regexp `fips`
4840+ And I verify that `strongswan-hmac` installed version matches regexp `fips`
4841+ When I run `apt-mark unhold openssh-client openssh-server strongswan` with sudo
4842+ Then I will see the following on stdout:
4843+ """
4844+ openssh-client was already not hold.
4845+ openssh-server was already not hold.
4846+ strongswan was already not hold.
4847+ """
4848+ When I run `ua status --all` with sudo
4849+ Then stdout matches regexp:
4850+ """
4851+ <fips-service> +yes disabled
4852+ """
4853+
4854+ Examples: ubuntu release
4855+ | release | fips-name | fips-service |fips-apt-source |
4856+ | xenial | FIPS Updates | fips-updates |https://esm.staging.ubuntu.com/fips-updates/ubuntu xenial-updates/main |
4857+ | bionic | FIPS Updates | fips-updates |https://esm.staging.ubuntu.com/fips-updates/ubuntu bionic-updates/main |
4858+
4859+ @series.xenial
4860+ @uses.config.machine_type.lxd.vm
4861+ Scenario Outline: Attached FIPS upgrade across LTS releases
4862+ Given a `<release>` machine with ubuntu-advantage-tools installed
4863+ When I attach `contract_token_staging` with sudo
4864+ And I run `apt-get install lsof` with sudo, retrying exit [100]
4865+ And I run `ua disable livepatch` with sudo
4866+ And I run `ua enable <fips-service> --assume-yes` with sudo
4867+ Then stdout matches regexp:
4868+ """
4869+ Updating package lists
4870+ Installing <fips-name> packages
4871+ <fips-name> enabled
4872+ A reboot is required to complete install
4873+ """
4874+ When I run `ua status --all` with sudo
4875+ Then stdout matches regexp:
4876+ """
4877+ <fips-service> +yes enabled
4878+ """
4879+ And I verify that running `apt update` `with sudo` exits `0`
4880+ When I reboot the `<release>` machine
4881+ And I run `uname -r` as non-root
4882+ Then stdout matches regexp:
4883+ """
4884+ fips
4885+ """
4886+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
4887+ Then I will see the following on stdout:
4888+ """
4889+ 1
4890+ """
4891+ When I run `apt-get dist-upgrade -y --allow-downgrades` with sudo
4892+ # A package may need a reboot after running dist-upgrade
4893+ And I reboot the `<release>` machine
4894+ And I create the file `/etc/update-manager/release-upgrades.d/ua-test.cfg` with the following
4895+ """
4896+ [Sources]
4897+ AllowThirdParty=yes
4898+ """
4899+ Then I verify that running `do-release-upgrade --frontend DistUpgradeViewNonInteractive` `with sudo` exits `0`
4900+ When I reboot the `<release>` machine
4901+ And I run `lsb_release -cs` as non-root
4902+ Then I will see the following on stdout:
4903+ """
4904+ <next_release>
4905+ """
4906+ When I verify that running `egrep "disabled" /etc/apt/sources.list.d/<source-file>.list` `as non-root` exits `1`
4907+ Then I will see the following on stdout:
4908+ """
4909+ """
4910+ When I run `ua status --all` with sudo
4911+ Then stdout matches regexp:
4912+ """
4913+ <fips-service> +yes enabled
4914+ """
4915+ When I run `uname -r` as non-root
4916+ Then stdout matches regexp:
4917+ """
4918+ fips
4919+ """
4920+ When I run `cat /proc/sys/crypto/fips_enabled` with sudo
4921+ Then I will see the following on stdout:
4922+ """
4923+ 1
4924+ """
4925+
4926+ Examples: ubuntu release
4927+ | release | next_release | fips-service | fips-name | source-file |
4928+ | xenial | bionic | fips | FIPS | ubuntu-fips |
4929+ | xenial | bionic | fips-updates | FIPS Updates | ubuntu-fips-updates |
4930+
4931+ @series.xenial
4932+ @series.bionic
4933+ Scenario Outline: Attached enable of cis service in a ubuntu machine
4934+ Given a `<release>` machine with ubuntu-advantage-tools installed
4935+ When I attach `contract_token_staging` with sudo
4936+ And I verify that running `ua enable cis --beta` `with sudo` exits `0`
4937+ Then I will see the following on stdout:
4938+ """
4939+ One moment, checking your subscription first
4940+ Updating package lists
4941+ Installing CIS Audit packages
4942+ CIS Audit enabled
4943+ """
4944+ When I run `apt-cache policy usg-cisbenchmark` as non-root
4945+ Then stdout does not match regexp:
4946+ """
4947+ .*Installed: \(none\)
4948+ """
4949+ And stdout matches regexp:
4950+ """
4951+ \s* 500 https://esm.staging.ubuntu.com/cis/ubuntu <release>/main amd64 Packages
4952+ """
4953+ When I run `apt-cache policy usg-common` as non-root
4954+ Then stdout does not match regexp:
4955+ """
4956+ .*Installed: \(none\)
4957+ """
4958+ And stdout matches regexp:
4959+ """
4960+ \s* 500 https://esm.staging.ubuntu.com/cis/ubuntu <release>/main amd64 Packages
4961+ """
4962+
4963+ Examples: not entitled services
4964+ | release |
4965+ | bionic |
4966+ | xenial |
4967+
4968+ @series.xenial
4969+ @series.bionic
4970+ @uses.config.machine_type.lxd.vm
4971+ Scenario Outline: Attached enable fips-updates on fips enabled vm
4972+ Given a `<release>` machine with ubuntu-advantage-tools installed
4973+ When I attach `contract_token_staging` with sudo
4974+ And I run `ua disable livepatch` with sudo
4975+ And I run `apt-get install openssh-client openssh-server strongswan -y` with sudo, retrying exit [100]
4976+ And I run `ua enable fips --assume-yes` with sudo
4977+ Then stdout matches regexp:
4978+ """
4979+ Updating package lists
4980+ Installing FIPS packages
4981+ FIPS enabled
4982+ A reboot is required to complete install
4983+ """
4984+ When I run `ua status --all` with sudo
4985+ Then stdout matches regexp:
4986+ """
4987+ fips +yes enabled
4988+ """
4989+ When I reboot the `<release>` machine
4990+ And I run `ua enable fips-updates --assume-yes` with sudo
4991+ Then stdout matches regexp:
4992+ """
4993+ Updating package lists
4994+ Installing FIPS Updates packages
4995+ FIPS Updates enabled
4996+ A reboot is required to complete install
4997+ """
4998+ When I run `ua status --all` with sudo
4999+ Then stdout matches regexp:
5000+ """
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: