tests: VM updates (direct and through updates proxy)
This commit is contained in:
		
							parent
							
								
									de295136ce
								
							
						
					
					
						commit
						9cb62f8f4d
					
				
							
								
								
									
										314
									
								
								tests/network.py
									
									
									
									
									
								
							
							
						
						
									
										314
									
								
								tests/network.py
									
									
									
									
									
								
							| @ -326,6 +326,315 @@ class VmNetworkingMixin(qubes.tests.SystemTestsMixin): | |||||||
|         self.assertNotEqual(self.run_cmd(self.testvm1, self.ping_ip), 0, |         self.assertNotEqual(self.run_cmd(self.testvm1, self.ping_ip), 0, | ||||||
|                          "Spoofed ping should be blocked") |                          "Spoofed ping should be blocked") | ||||||
| 
 | 
 | ||||||
|  | class VmUpdatesMixin(qubes.tests.SystemTestsMixin): | ||||||
|  |     """ | ||||||
|  |     Tests for VM updates | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     # made this way to work also when no package build tools are installed | ||||||
|  |     """ | ||||||
|  |     $ cat test-pkg.spec: | ||||||
|  |     Name:		test-pkg | ||||||
|  |     Version:	1.0 | ||||||
|  |     Release:	1%{?dist} | ||||||
|  |     Summary:	Test package | ||||||
|  | 
 | ||||||
|  |     Group:		System | ||||||
|  |     License:	GPL | ||||||
|  |     URL:		http://example.com/ | ||||||
|  | 
 | ||||||
|  |     %description | ||||||
|  |     Test package | ||||||
|  | 
 | ||||||
|  |     %files | ||||||
|  | 
 | ||||||
|  |     %changelog | ||||||
|  |     $ rpmbuild -bb test-pkg.spec | ||||||
|  |     $ cat test-pkg-1.0-1.fc21.x86_64.rpm | gzip | base64 | ||||||
|  |     """ | ||||||
|  |     RPM_PACKAGE_GZIP_BASE64 = ( | ||||||
|  |         "H4sIAPzRLlYAA+2Y728URRjHn7ueUCkERKJVJDnTxLSxs7293o8WOER6ljYYrtKCLUSa3" | ||||||
|  |         "bnZ64bd22VmTq8nr4wJbwxvjNHIG0x8oTHGGCHB8AcYE1/0lS80GgmQFCJU3wgB4ZjdfZ" | ||||||
|  |         "q2xDe8NNlvMjfzmeeZH7tPbl98b35169cOUEpIJiTxT9SIrmVUs2hWh8dUAp54dOrM14s" | ||||||
|  |         "JHK4D2DKl+j2qrVfjsuq3qEWbohjuAB2Lqk+p1o/8Z5QPmSi/YwnjezH+F8bLQZjqllW0" | ||||||
|  |         "hvODRmFIL5hFk9JMXi/mi5ZuDleNwSEzP5wtmLnouNQnm3/6fndz7FLt9M/Hruj37gav4" | ||||||
|  |         "tTjPnasWLFixYoVK1asWLFixYoV63+p0KNot9vnIPQc1vgYOwCSgXfxCoS+QzKHOVXVOj" | ||||||
|  |         "Fn2ccIfI0k8nXkLuQbyJthxed4UrVnkG8i9yDfgsj3yCAv4foc8t+w1hf5B+Nl5Du43xj" | ||||||
|  |         "yvxivIN9HpsgPkO2IU9uQfeRn8Xk/iJ4x1Y3nfxH1qecwfhH5+YgT25F7o/0SRdxvOppP" | ||||||
|  |         "7MX9ZjB/DNnE/OOYX404uRGZIT+FbCFvQ3aQ8f0+/WF0XjJ8nyOw7H+BrmUA/a8pNZf2D" | ||||||
|  |         "XrCqLG1cERbWHI8ajhznpBY9P0Tr8PkvJDMhTkp/Z0DA6xpuL7DNOq5A+DY9UYTmkOF2U" | ||||||
|  |         "IO/sNt0wSnGvfdlZssD3rVIlLI9UUX37C6qXzHNntHPNfnTAhWHbUddtBwmegDjAUzZbu" | ||||||
|  |         "m9lqZmzDmHc8Ik8WY8Tab4Myym4+Gx8V0qw8GtYyWIzrktEJwV9UHv3ktG471rAqHTmFQ" | ||||||
|  |         "685V5uGqIalk06SWJr7tszR503Ac9cs493jJ8rhrSCIYbXBbzqt5v5+UZ0crh6bGR2dmJ" | ||||||
|  |         "yuHD428VlLLLdakzJe2VxcKhFSFID73JKPS40RI7tXVCcQ3uOGWhPCJ2bAspiJ2i5Vy6n" | ||||||
|  |         "jOqMerpEYpEe/Yks4xkU4Tt6BirmzUWanG6ozbFKhve9BsQRaLRTirzqk7hgUktXojKnf" | ||||||
|  |         "n8jeg3X4QepP3i63po6oml+9t/CwJLya2Bn/ei6f7/4B3Ycdb0L3pt5Q5mNz16rWJ9fLk" | ||||||
|  |         "vvOff/nxS7//8O2P2gvt7nDDnoV9L1du9N4+ucjl9u/8+a7dC5Nnvjlv9Ox5r+v9Cy0NE" | ||||||
|  |         "m+c6rv60S/dZw98Gn6MNswcfQiWUvg3wBUAAA==" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     Minimal package generated by running dh_make on empty directory | ||||||
|  |     Then cat test-pkg_1.0-1_amd64.deb | gzip | base64 | ||||||
|  |     """ | ||||||
|  |     DEB_PACKAGE_GZIP_BASE64 = ( | ||||||
|  |         "H4sIACTXLlYAA1O0SSxKzrDjSklNykzM003KzEssqlRQUDA0MTG1NDQwNDVTUDBQAAEIa" | ||||||
|  |         "WhgYGZioqBgogADCVxGegZcyfl5JUX5OXoliUV66VVE6DcwheuX7+ZgAAEW5rdXHb0PG4" | ||||||
|  |         "iwf5j3WfMT6zWzzMuZgoE3jjYraNzbbFKWGms0SaRw/r2SV23WZ4IdP8preM4yqf0jt95" | ||||||
|  |         "3c8qnacfNxJUkf9/w+/3X9ph2GEdgQdixrz/niHKKTnYXizf4oSC7tHOz2Zzq+/6vn8/7" | ||||||
|  |         "ezQ7c1tmi7xZ3SGJ4yzhT2dcr7V+W3zM5ZPu/56PSv4Zdok+7Yv/V/6buWaKVlFkkV58S" | ||||||
|  |         "N3GmLgnqzRmeZ3V3ymmurS5fGa85/LNx1bpZMin3S6dvXKqydp3ubP1vmyarJZb/qSh62" | ||||||
|  |         "C8oIdxqm/BtvkGDza+On/Vfv2py7/0LV7VH+qR6a+bkKUbHXt5/SG187d+nps1a5PJfMO" | ||||||
|  |         "i11dWcUe1HjwaW3Q5RHXn9LmcHy+tW9YcKf0768XVB1t3R0bKrzs5t9P+6r7rZ99svH10" | ||||||
|  |         "+Q6F/o8tf1fO/32y+fWa14eifd+WxUy0jcxYH7N9/tUvmnUZL74pW32qLeuRU+ZwYGASa" | ||||||
|  |         "GBgUWBgxM90ayy3VdmykkGDgYErJbEkERydFVWQmCMQo8aWZvAY/WteFRHFwMCYqXTPjI" | ||||||
|  |         "lBkVEMGLsl+k8XP1D/z+gXyyDOvUemlnHqAVkvu0rRQ2fUFodkN3mtU9uwhqk8V+TqPEE" | ||||||
|  |         "Nc7fzoQ4n71lqRs/7kbbT0+qOZuKH4r8mjzsc1k/YkCHN8Pjg48fbpE+teHa96LNcfu0V" | ||||||
|  |         "5n2/Z2xa2KDvaCOx8cqBFxc514uZ3TmadXS+6cpzU7wSzq5SWfapJOD9n6wLXSwtlgxZh" | ||||||
|  |         "xITzWW7buhx/bb291RcVlEfeC9K5hlrqunSzIMSZT7/Nqgc/qMvMNW227WI8ezB8mVuZh" | ||||||
|  |         "0hERJSvysfburr4Dx0I9BW57UwR4+e1gxu49PcEt8sbK18Xpvt//Hj5UYm+Zc25q+T4xl" | ||||||
|  |         "rJvxfVnh80oadq57OZxPaU1bbztv1yF365W4t45Yr+XrFzov237GVY1Zgf7NvE4+W2SuR" | ||||||
|  |         "lQtLauR1TQ/mbOiIONYya6tU1jPGpWfk/i1+ttiXe3ZO14n0YOWggndznjGlGLyfVbBC6" | ||||||
|  |         "MRP5aMM7aCco/s7sZqB8RlTQwADw8rnuT/sDHi7mUASjJFRAAbWwNLiAwAA" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def run_cmd(self, vm, cmd, user="root"): | ||||||
|  |         p = vm.run(cmd, user=user, passio_popen=True, ignore_stderr=True) | ||||||
|  |         p.stdin.close() | ||||||
|  |         p.stdout.read() | ||||||
|  |         return p.wait() | ||||||
|  | 
 | ||||||
|  |     def setUp(self): | ||||||
|  |         super(VmUpdatesMixin, self).setUp() | ||||||
|  | 
 | ||||||
|  |         self.update_cmd = None | ||||||
|  |         if self.template.count("debian"): | ||||||
|  |             self.update_cmd = "set -o pipefail; apt-get update 2>&1 | " \ | ||||||
|  |                               "{ ! grep '^W:\|^E:'; }" | ||||||
|  |             self.install_cmd = "apt-get install -y {}" | ||||||
|  |             self.install_test_cmd = "dpkg -l {}" | ||||||
|  |             self.exit_code_ok = [0] | ||||||
|  |         elif self.template.count("fedora"): | ||||||
|  |             cmd = "yum" | ||||||
|  |             try: | ||||||
|  |                 # assume template name in form "fedora-XX-suffix" | ||||||
|  |                 if int(self.template.split("-")[1]) > 21: | ||||||
|  |                     cmd = "dnf" | ||||||
|  |             except ValueError: | ||||||
|  |                 pass | ||||||
|  |             self.update_cmd = "{cmd} clean all; {cmd} check-update".format( | ||||||
|  |                 cmd=cmd) | ||||||
|  |             self.install_cmd = cmd + " install -y {}" | ||||||
|  |             self.install_test_cmd = "rpm -q {}" | ||||||
|  |             self.exit_code_ok = [0, 100] | ||||||
|  |         else: | ||||||
|  |             self.skipTest("Template {} not supported by this test".format( | ||||||
|  |                 self.template)) | ||||||
|  | 
 | ||||||
|  |         self.testvm1 = self.qc.add_new_vm( | ||||||
|  |             "QubesAppVm", | ||||||
|  |             name=self.make_vm_name('vm1'), | ||||||
|  |             template=self.qc.get_vm_by_name(self.template)) | ||||||
|  |         self.testvm1.create_on_disk(verbose=False) | ||||||
|  | 
 | ||||||
|  |     def test_000_simple_update(self): | ||||||
|  |         self.save_and_reload_db() | ||||||
|  |         self.qc.unlock_db() | ||||||
|  |         # reload the VM to have all the properties properly set (especially | ||||||
|  |         # default netvm) | ||||||
|  |         self.testvm1 = self.qc[self.testvm1.qid] | ||||||
|  |         self.testvm1.start() | ||||||
|  |         p = self.testvm1.run(self.update_cmd, wait=True, user="root", | ||||||
|  |                              passio_popen=True, passio_stderr=True) | ||||||
|  |         (stdout, stderr) = p.communicate() | ||||||
|  |         self.assertIn(p.wait(), self.exit_code_ok, | ||||||
|  |                       "{}: {}\n{}".format(self.update_cmd, stdout, stderr) | ||||||
|  |                       ) | ||||||
|  | 
 | ||||||
|  |     def create_repo_apt(self): | ||||||
|  |         pkg_file_name = "test-pkg_1.0-1_amd64.deb" | ||||||
|  |         p = self.netvm_repo.run("mkdir /tmp/apt-repo && cd /tmp/apt-repo &&" | ||||||
|  |                                 "base64 -d | zcat > {}".format(pkg_file_name), | ||||||
|  |                                 passio_popen=True) | ||||||
|  |         p.stdin.write(self.DEB_PACKAGE_GZIP_BASE64) | ||||||
|  |         p.stdin.close() | ||||||
|  |         if p.wait() != 0: | ||||||
|  |             raise RuntimeError("Failed to write {}".format(pkg_file_name)) | ||||||
|  |         # do not assume dpkg-scanpackage installed | ||||||
|  |         packages_path = "dists/test/main/binary-amd64/Packages" | ||||||
|  |         p = self.netvm_repo.run( | ||||||
|  |             "mkdir -p /tmp/apt-repo/dists/test/main/binary-amd64 && " | ||||||
|  |             "cd /tmp/apt-repo && " | ||||||
|  |             "cat > {packages} && " | ||||||
|  |             "echo MD5sum: $(openssl md5 -r {pkg} | cut -f 1 -d ' ')" | ||||||
|  |             " >> {packages} && " | ||||||
|  |             "echo SHA1: $(openssl sha1 -r {pkg} | cut -f 1 -d ' ')" | ||||||
|  |             " >> {packages} && " | ||||||
|  |             "echo SHA256: $(openssl sha256 -r {pkg} | cut -f 1 -d ' ')" | ||||||
|  |             " >> {packages} && " | ||||||
|  |             "gzip < {packages} > {packages}.gz".format(pkg=pkg_file_name, | ||||||
|  |                                                        packages=packages_path), | ||||||
|  |             passio_popen=True, passio_stderr=True) | ||||||
|  |         p.stdin.write( | ||||||
|  |             "Package: test-pkg\n" | ||||||
|  |             "Version: 1.0-1\n" | ||||||
|  |             "Architecture: amd64\n" | ||||||
|  |             "Maintainer: unknown <user@host>\n" | ||||||
|  |             "Installed-Size: 25\n" | ||||||
|  |             "Filename: {pkg}\n" | ||||||
|  |             "Size: 994\n" | ||||||
|  |             "Section: unknown\n" | ||||||
|  |             "Priority: optional\n" | ||||||
|  |             "Description: Test package\n".format(pkg=pkg_file_name) | ||||||
|  |         ) | ||||||
|  |         p.stdin.close() | ||||||
|  |         if p.wait() != 0: | ||||||
|  |             raise RuntimeError("Failed to write Packages file: {}".format( | ||||||
|  |                 p.stderr.read())) | ||||||
|  | 
 | ||||||
|  |         p = self.netvm_repo.run( | ||||||
|  |             "mkdir -p /tmp/apt-repo/dists/test && " | ||||||
|  |             "cd /tmp/apt-repo/dists/test && " | ||||||
|  |             "cat > Release <<EOF && " | ||||||
|  |             "echo '' $(sha1sum {p} | cut -f 1 -d ' ') $(stat -c %s {p}) {p}" | ||||||
|  |             " >> Release && " | ||||||
|  |             "echo '' $(sha1sum {z} | cut -f 1 -d ' ') $(stat -c %s {z}) {z}" | ||||||
|  |             " >> Release" | ||||||
|  |             .format(p="main/binary-amd64/Packages", | ||||||
|  |                     z="main/binary-amd64/Packages.gz"), | ||||||
|  |             passio_popen=True, passio_stderr=True | ||||||
|  |         ) | ||||||
|  |         p.stdin.write( | ||||||
|  |             "Label: Test repo\n" | ||||||
|  |             "Suite: test\n" | ||||||
|  |             "Codename: test\n" | ||||||
|  |             "Date: Tue, 27 Oct 2015 03:22:09 +0100\n" | ||||||
|  |             "Architectures: amd64\n" | ||||||
|  |             "Components: main\n" | ||||||
|  |             "SHA1:\n" | ||||||
|  |             "EOF\n" | ||||||
|  |         ) | ||||||
|  |         p.stdin.close() | ||||||
|  |         if p.wait() != 0: | ||||||
|  |             raise RuntimeError("Failed to write Release file: {}".format( | ||||||
|  |                 p.stderr.read())) | ||||||
|  | 
 | ||||||
|  |     def create_repo_yum(self): | ||||||
|  |         pkg_file_name = "test-pkg-1.0-1.fc21.x86_64.rpm" | ||||||
|  |         p = self.netvm_repo.run("mkdir /tmp/yum-repo && cd /tmp/yum-repo &&" | ||||||
|  |                                 "base64 -d | zcat > {}".format(pkg_file_name), | ||||||
|  |                                 passio_popen=True, passio_stderr=True) | ||||||
|  |         p.stdin.write(self.RPM_PACKAGE_GZIP_BASE64) | ||||||
|  |         p.stdin.close() | ||||||
|  |         if p.wait() != 0: | ||||||
|  |             raise RuntimeError("Failed to write {}: {}".format(pkg_file_name, | ||||||
|  |                                                                p.stderr.read())) | ||||||
|  | 
 | ||||||
|  |         # createrepo is installed by default in Fedora template | ||||||
|  |         p = self.netvm_repo.run("createrepo /tmp/yum-repo", | ||||||
|  |                                 passio_popen=True, | ||||||
|  |                                 passio_stderr=True) | ||||||
|  |         if p.wait() != 0: | ||||||
|  |             raise RuntimeError("Failed to create yum metadata: {}".format( | ||||||
|  |                 p.stderr.read())) | ||||||
|  | 
 | ||||||
|  |     def create_repo_and_serve(self): | ||||||
|  |         if self.template.count("debian") or self.template.count("whonix"): | ||||||
|  |             self.create_repo_apt() | ||||||
|  |             self.netvm_repo.run("cd /tmp/apt-repo &&" | ||||||
|  |                                 "python -m SimpleHTTPServer 8080") | ||||||
|  |         elif self.template.count("fedora"): | ||||||
|  |             self.create_repo_yum() | ||||||
|  |             self.netvm_repo.run("cd /tmp/yum-repo &&" | ||||||
|  |                                 "python -m SimpleHTTPServer 8080") | ||||||
|  |         else: | ||||||
|  |             # not reachable... | ||||||
|  |             self.skipTest("Template {} not supported by this test".format( | ||||||
|  |                 self.template)) | ||||||
|  | 
 | ||||||
|  |     def configure_test_repo(self): | ||||||
|  |         """ | ||||||
|  |         Configure test repository in test-vm and disable rest of them. | ||||||
|  |         The critical part is to use "localhost" - this will work only when | ||||||
|  |         accessed through update proxy and this is exactly what we want to | ||||||
|  |         test here. | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         if self.template.count("debian") or self.template.count("whonix"): | ||||||
|  |             self.testvm1.run( | ||||||
|  |                 "rm -f /etc/apt/sources.list.d/* &&" | ||||||
|  |                 "echo 'deb [trusted=yes] http://localhost:8080 test main' " | ||||||
|  |                 "> /etc/apt/sources.list", | ||||||
|  |                 user="root") | ||||||
|  |         elif self.template.count("fedora"): | ||||||
|  |             self.testvm1.run( | ||||||
|  |                 "rm -f /etc/yum.repos.d/*.repo &&" | ||||||
|  |                 "echo '[test]' > /etc/yum.repos.d/test.repo &&" | ||||||
|  |                 "echo 'name=Test repo' >> /etc/yum.repos.d/test.repo &&" | ||||||
|  |                 "echo 'gpgcheck=0' >> /etc/yum.repos.d/test.repo &&" | ||||||
|  |                 "echo 'baseurl=http://localhost:8080/'" | ||||||
|  |                 " >> /etc/yum.repos.d/test.repo", | ||||||
|  |                 user="root" | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             # not reachable... | ||||||
|  |             self.skipTest("Template {} not supported by this test".format( | ||||||
|  |                 self.template)) | ||||||
|  | 
 | ||||||
|  |     def test_010_update_via_proxy(self): | ||||||
|  |         """ | ||||||
|  |         Test both whether updates proxy works and whether is actually used by the VM | ||||||
|  |         """ | ||||||
|  |         if self.template.count("minimal"): | ||||||
|  |             self.skipTest("Template {} not supported by this test".format( | ||||||
|  |                 self.template)) | ||||||
|  | 
 | ||||||
|  |         self.netvm_repo = self.qc.add_new_vm( | ||||||
|  |             "QubesNetVm", | ||||||
|  |             name=self.make_vm_name('net'), | ||||||
|  |             template=self.qc.get_vm_by_name(self.template)) | ||||||
|  |         self.netvm_repo.create_on_disk(verbose=False) | ||||||
|  |         self.testvm1.netvm = self.netvm_repo | ||||||
|  |         # NetVM should have qubes-updates-proxy enabled by default | ||||||
|  |         #self.netvm_repo.services['qubes-updates-proxy'] = True | ||||||
|  |         # TODO: consider also adding a test for the template itself | ||||||
|  |         self.testvm1.services['updates-proxy-setup'] = True | ||||||
|  |         self.qc.save() | ||||||
|  |         self.qc.unlock_db() | ||||||
|  | 
 | ||||||
|  |         # Setup test repo | ||||||
|  |         self.netvm_repo.start() | ||||||
|  |         self.create_repo_and_serve() | ||||||
|  | 
 | ||||||
|  |         # Configure local repo | ||||||
|  |         self.testvm1.start() | ||||||
|  |         self.configure_test_repo() | ||||||
|  | 
 | ||||||
|  |         # update repository metadata | ||||||
|  |         p = self.testvm1.run(self.update_cmd, wait=True, user="root", | ||||||
|  |                              passio_popen=True, passio_stderr=True) | ||||||
|  |         (stdout, stderr) = p.communicate() | ||||||
|  |         self.assertIn(p.wait(), self.exit_code_ok, | ||||||
|  |                       "{}: {}\n{}".format(self.update_cmd, stdout, stderr) | ||||||
|  |                       ) | ||||||
|  | 
 | ||||||
|  |         # install test package | ||||||
|  |         p = self.testvm1.run(self.install_cmd.format('test-pkg'), | ||||||
|  |                              wait=True, user="root", | ||||||
|  |                              passio_popen=True, passio_stderr=True) | ||||||
|  |         (stdout, stderr) = p.communicate() | ||||||
|  |         self.assertIn(p.wait(), self.exit_code_ok, | ||||||
|  |                       "{}: {}\n{}".format(self.update_cmd, stdout, stderr) | ||||||
|  |                       ) | ||||||
|  | 
 | ||||||
|  |         # verify if it was really installed | ||||||
|  |         p = self.testvm1.run(self.install_test_cmd.format('test-pkg'), | ||||||
|  |                              wait=True, user="root", | ||||||
|  |                              passio_popen=True, passio_stderr=True) | ||||||
|  |         (stdout, stderr) = p.communicate() | ||||||
|  |         self.assertIn(p.wait(), self.exit_code_ok, | ||||||
|  |                       "{}: {}\n{}".format(self.update_cmd, stdout, stderr) | ||||||
|  |                       ) | ||||||
| 
 | 
 | ||||||
| def load_tests(loader, tests, pattern): | def load_tests(loader, tests, pattern): | ||||||
|     try: |     try: | ||||||
| @ -343,4 +652,9 @@ def load_tests(loader, tests, pattern): | |||||||
|                 'VmNetworking_' + template, |                 'VmNetworking_' + template, | ||||||
|                 (VmNetworkingMixin, qubes.tests.QubesTestCase), |                 (VmNetworkingMixin, qubes.tests.QubesTestCase), | ||||||
|                 {'template': template}))) |                 {'template': template}))) | ||||||
|  |         tests.addTests(loader.loadTestsFromTestCase( | ||||||
|  |             type( | ||||||
|  |                 'VmUpdates_' + template, | ||||||
|  |                 (VmUpdatesMixin, qubes.tests.QubesTestCase), | ||||||
|  |                 {'template': template}))) | ||||||
|     return tests |     return tests | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki