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, | ||||
|                          "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): | ||||
|     try: | ||||
| @ -343,4 +652,9 @@ def load_tests(loader, tests, pattern): | ||||
|                 'VmNetworking_' + template, | ||||
|                 (VmNetworkingMixin, qubes.tests.QubesTestCase), | ||||
|                 {'template': template}))) | ||||
|         tests.addTests(loader.loadTestsFromTestCase( | ||||
|             type( | ||||
|                 'VmUpdates_' + template, | ||||
|                 (VmUpdatesMixin, qubes.tests.QubesTestCase), | ||||
|                 {'template': template}))) | ||||
|     return tests | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki