def setUp(self): super(UsersTest, self).setUp() self.useFixture(AptInstalledJenkins(self.fakes)) self.fakes.jenkins.scripts[GET_LEGACY_TOKEN_SCRIPT.format( "admin")] = "abc\n" self.apt = AptStub() self.packages = Packages(apt=self.apt) self.users = Users(packages=self.packages)
def setUp(self): super(ApiTest, self).setUp() self.useFixture(JenkinsConfiguredAdmin(self.fakes)) self.fakes.jenkins.scripts[GET_LEGACY_TOKEN_SCRIPT.format( "admin")] = "abc\n" self.fakes.jenkins.scripts[GET_NEW_TOKEN_SCRIPT.format( "admin")] = "xyz\n" self.apt = AptStub() self.packages = Packages(apt=self.apt) self.api = Api(packages=self.packages)
def setUp(self): super(PackagesTest, self).setUp() self.apt = AptStub() self.ch_host = CharmHelpersCoreHostStub() self.packages = Packages(apt=self.apt, ch_host=self.ch_host) # XXX Not all charm files are populated in charm_dir() by default. # XXX See: https://github.com/freeekanayaka/charm-test/issues/2 keyfile = "jenkins.io.key" os.symlink(os.path.join(os.getcwd(), keyfile), os.path.join(hookenv.charm_dir(), keyfile))
class UsersTest(JenkinsTest): def setUp(self): super(UsersTest, self).setUp() self.useFixture(AptInstalledJenkins(self.fakes)) self.fakes.jenkins.scripts[GET_LEGACY_TOKEN_SCRIPT.format( "admin")] = "abc\n" self.apt = AptStub() self.packages = Packages(apt=self.apt) self.users = Users(packages=self.packages) def test_configure_admin_custom_password(self): """ If a password is provided, it's used to configure the admin user. """ self.apt._set_jenkins_version('2.120.1') config = hookenv.config() orig_password = config["password"] try: config["password"] = "******" script = UPDATE_PASSWORD_SCRIPT.format(username="******", password="******") self.fakes.jenkins.scripts[script] = "" self.users.configure_admin() self.assertThat(paths.ADMIN_PASSWORD, FileContains("x")) self.assertThat(paths.ADMIN_PASSWORD, HasOwnership(0, 0)) self.assertThat(paths.ADMIN_PASSWORD, HasPermissions("0600")) self.assertThat(paths.LAST_EXEC, FileContains("2.0.0\n")) self.assertThat(paths.LAST_EXEC, HasOwnership(123, 456)) finally: config["password"] = orig_password def test_configure_admin_random_password(self): """ If a password is not provided, a random one will be generated. """ def pwgen(length): return "z" self.apt._set_jenkins_version('2.120.1') script = UPDATE_PASSWORD_SCRIPT.format(username="******", password="******") self.fakes.jenkins.scripts[script] = "" self.useFixture(MonkeyPatch("charmhelpers.core.host.pwgen", pwgen)) self.users.configure_admin() self.assertTrue(hookenv.config()["_generated-password"])
def setUp(self): super(PackagesTest, self).setUp() self.apt = AptStub() self.ch_host = CharmHelpersCoreHostStub() self.packages = Packages(apt=self.apt, ch_host=self.ch_host) # XXX Not all charm files are populated in charm_dir() by default. # XXX See: https://github.com/freeekanayaka/charm-test/issues/2 keyfile = "jenkins.io.key" os.symlink(os.path.join(os.getcwd(), keyfile), os.path.join(hookenv.charm_dir(), keyfile)) jenkins_cache_dir = "/var/cache/jenkins/war/WEB-INF" self.fakes.fs.add(paths.PLUGINS) self.fakes.fs.add(jenkins_cache_dir) os.makedirs(paths.PLUGINS) os.makedirs(jenkins_cache_dir)
class ApiTest(JenkinsTest): def setUp(self): super(ApiTest, self).setUp() self.useFixture(JenkinsConfiguredAdmin(self.fakes)) self.fakes.jenkins.scripts[GET_LEGACY_TOKEN_SCRIPT.format( "admin")] = "abc\n" self.fakes.jenkins.scripts[GET_NEW_TOKEN_SCRIPT.format( "admin")] = "xyz\n" self.apt = AptStub() self.packages = Packages(apt=self.apt) self.api = Api(packages=self.packages) def test_wait_transient_failure(self): """ Wait for Jenkins to be fully up, even in spite of transient failures. """ self.apt._set_jenkins_version('2.120.1') get_whoami = self.fakes.jenkins.get_whoami tries = [] def transient_failure(): try: if not tries: raise JenkinsException("error") get_whoami() finally: tries.append(True) self.fakes.jenkins.get_whoami = transient_failure self.assertIsNone(self.api.wait()) def test_update_password(self): """ The update_password() method runs a groovy script to update the password for the given user. """ self.apt._set_jenkins_version('2.120.1') username = "******" password = "******" script = UPDATE_PASSWORD_SCRIPT.format(username=username, password=password) self.fakes.jenkins.scripts[script] = "" self.assertIsNone(self.api.update_password(username, password)) def test_version(self): """The version() method returns the version of the Jenkins server.""" self.apt._set_jenkins_version('2.120.1') self.assertEqual("2.0.0", self.api.version()) def test_new_token_script(self): self.apt._set_jenkins_version('2.150.1') self.assertEqual("2.0.0", self.api.version()) def test_add(self): """ A slave node can be added by specifying executors and labels. """ self.apt._set_jenkins_version('2.120.1') self.api.add_node("slave-0", 1, labels=["python"]) [node] = self.fakes.jenkins.nodes self.assertEqual("slave-0", node.host) self.assertEqual(1, node.executors) self.assertEqual("slave-0", node.description) self.assertEqual(["python"], node.labels) self.assertEqual("hudson.slaves.JNLPLauncher", node.launcher) def test_add_exists(self): """ If a node already exists, nothing is done. """ self.apt._set_jenkins_version('2.120.1') self.fakes.jenkins.create_node("slave-0", 1, "slave-0") self.api.add_node("slave-0", 1, labels=["python"]) self.assertEqual(1, len(self.fakes.jenkins.nodes)) def test_add_transient_failure(self): """ Transient failures get retried. """ self.apt._set_jenkins_version('2.120.1') create_node = self.fakes.jenkins.create_node tries = [] def transient_failure(*args, **kwargs): try: if not tries: raise JenkinsException("error") create_node(*args, **kwargs) finally: tries.append(True) self.fakes.jenkins.create_node = transient_failure self.api.add_node("slave-0", 1, labels=["python"]) self.assertEqual(1, len(self.fakes.jenkins.nodes)) def test_add_retry_give_up(self): """ If errors persist, we give up. """ self.apt._set_jenkins_version('2.120.1') def failure(*args, **kwargs): raise JenkinsException("error") self.fakes.jenkins.create_node = failure self.assertRaises(JenkinsException, self.api.add_node, "slave-0", 1) def test_add_spurious(self): """ If adding a node apparently succeeds, but actually didn't then we log an error. """ self.apt._set_jenkins_version('2.120.1') self.fakes.jenkins.create_node = lambda *args, **kwargs: None self.api.add_node("slave-0", 1, labels=["python"]) self.assertEqual("ERROR: Failed to create node 'slave-0'", self.fakes.juju.log[-1]) def test_deleted(self): """ A slave node can be deleted by specifyng its host name. """ self.apt._set_jenkins_version('2.120.1') self.api.add_node("slave-0", 1, labels=["python"]) self.api.delete_node("slave-0") self.assertEqual([], self.fakes.jenkins.nodes) def test_deleted_no_present(self): """ If a slave node doesn't exists, deleting it is a no-op. """ self.apt._set_jenkins_version('2.120.1') self.api.delete_node("slave-0") self.assertEqual([], self.fakes.jenkins.nodes) def _make_httperror(self, url, status_code, reason): response = Response() response.reason = reason response.status_code = status_code response.url = url return HTTPError(request=Request('POST', url), response=response) def test_reload(self): """ The reload method POSTs a request to the '/reload' URL, expecting a 503 on the homepage (which happens after redirection). """ self.apt._set_jenkins_version('2.120.1') error = self._make_httperror(self.api.url, 503, "Service Unavailable") self.fakes.jenkins.responses[urljoin(self.api.url, "reload")] = error self.api.reload() def test_restart(self): """ The reload method POSTs a request to the '/reload' URL, expecting a 503 on the homepage (which happens after redirection). """ self.apt._set_jenkins_version('2.120.1') error = self._make_httperror(self.api.url, 503, "Service Unavailable") self.fakes.jenkins.responses[urljoin(self.api.url, "safeRestart")] = error self.api.restart() def test_reload_unexpected_error(self): """ If the error code is not 403, the error is propagated. """ self.apt._set_jenkins_version('2.120.1') error = self._make_httperror(self.api.url, 403, "Forbidden") self.fakes.jenkins.responses[urljoin(self.api.url, "reload")] = error self.assertRaises(HTTPError, self.api.reload) def test_reload_unexpected_url(self): """ If the error URL is not the root, the error is propagated. """ self.apt._set_jenkins_version('2.120.1') error = self._make_httperror(self.api.url, 503, "Service Unavailable") error.response.url = urljoin(self.api.url, "/foo") self.fakes.jenkins.responses[urljoin(self.api.url, "reload")] = error self.assertRaises(HTTPError, self.api.reload) def test_reload_unexpected_success(self): """ If the request unexpectedly succeeds, an error is raised. """ self.apt._set_jenkins_version('2.120.1') self.fakes.jenkins.responses[urljoin(self.api.url, "reload")] = "home" self.assertRaises(RuntimeError, self.api.reload) def test_url(self): """ Verify the url always ends in a / and has the expected prefix """ config = hookenv.config() orig_public_url = config["public-url"] try: config["public-url"] = "" self.assertEqual(self.api.url, 'http://localhost:8080/') config["public-url"] = "http://here:8080/jenkins" self.assertEqual(self.api.url, 'http://localhost:8080/jenkins/') finally: config["public-url"] = orig_public_url
class PackagesTest(CharmTest): def setUp(self): super(PackagesTest, self).setUp() self.apt = AptStub() self.ch_host = CharmHelpersCoreHostStub() self.packages = Packages(apt=self.apt, ch_host=self.ch_host) # XXX Not all charm files are populated in charm_dir() by default. # XXX See: https://github.com/freeekanayaka/charm-test/issues/2 keyfile = "jenkins.io.key" os.symlink(os.path.join(os.getcwd(), keyfile), os.path.join(hookenv.charm_dir(), keyfile)) jenkins_cache_dir = "/var/cache/jenkins/war/WEB-INF" self.fakes.fs.add(paths.PLUGINS) self.fakes.fs.add(jenkins_cache_dir) os.makedirs(paths.PLUGINS) os.makedirs(jenkins_cache_dir) def tearDown(self): # Reset installs and sources after each test super(PackagesTest, self).tearDown() self.packages.installs = [] self.packages.sources = [] def test_install_dependencies(self): """ The Jenkins dependencies get installed by the install_dependencies method. """ # Our default distro version (xenial). self.assertEqual(self.packages.distro_codename(), 'xenial') self.packages.install_dependencies() self.assertEqual(APT_DEPENDENCIES['xenial'], self.apt.installs) # Now check with a distro of bionic. self.apt.installs = [] self.ch_host._set_distro_version('bionic') self.assertEqual(self.packages.distro_codename(), 'bionic') self.packages.install_dependencies() self.assertEqual(APT_DEPENDENCIES['bionic'], self.apt.installs) def test_install_tools(self): """ The requested tools get installed by the install_tools method. """ orig_tools = hookenv.config()["tools"] try: hookenv.config()["tools"] = "git gcc" self.packages.install_tools() self.assertEqual(["git", "gcc"], self.apt.installs) finally: hookenv.config()["tools"] = orig_tools def test_install_jenkins_bundle(self): """ If the 'release' config is set to 'bundle', then Jenkins will be installed from a local jenkins.deb file. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "bundle" files = os.path.join(hookenv.charm_dir(), "files") os.mkdir(files) bundle_path = os.path.join(files, "jenkins.deb") with open(bundle_path, "w") as fd: fd.write("") self.packages.install_jenkins() self.assertEqual( ["install"], self.fakes.processes.dpkg.actions["jenkins"]) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_bundle_no_file(self): """ If the 'release' config is set to 'bundle' but no jenkins.deb file is present, an error is raised. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "bundle" path = os.path.join(hookenv.charm_dir(), "files", "jenkins.deb") self.assertThat(path, Not(PathExists())) error = self.assertRaises(Exception, self.packages._install_from_bundle) self.assertEqual( "'{}' doesn't exist. No package bundled.".format(path), str(error)) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_remote(self): """ If the 'release' config is set to a remote URL, then Jenkins will be installed from the deb files pointed by that url. """ self.fakes.processes.wget.locations[ "http://jenkins-1.2.3.deb"] = b"data" orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "http://jenkins-1.2.3.deb" self.packages.install_jenkins() self.assertEqual( ["install"], self.fakes.processes.dpkg.actions["jenkins"]) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_lts_release(self): """ If the 'release' config is set to 'lts', an APT source entry will be added, pointing to the debian-stable Jenkins repository. """ self.packages.install_jenkins() source = APT_SOURCE % "debian-stable" key = os.path.join(hookenv.charm_dir(), "jenkins.io.key") with open(key, "r") as k: key = k.read() self.assertEqual([(source, key)], self.apt.sources) def test_install_jenkins_trunk_release(self): """ If the 'release' config is set to 'trunk', an APT source entry will be added, pointing to the debian Jenkins repository. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "trunk" self.packages.install_jenkins() source = APT_SOURCE % "debian" key = os.path.join(hookenv.charm_dir(), "jenkins.io.key") with open(key, "r") as k: key = k.read() self.assertEqual([(source, key)], self.apt.sources) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_invalid_release(self): """ If the 'release' config is invalid, an error is raised. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "foo" error = self.assertRaises(Exception, self.packages.install_jenkins) self.assertEqual( "Release 'foo' configuration not recognised", str(error)) finally: hookenv.config()["release"] = orig_release def test_jenkins_version(self): self.assertEqual(self.packages.jenkins_version(), '2.150.1') # And now test older version. self.apt._set_jenkins_version('2.128.1') self.assertEqual(self.packages.jenkins_version(), '2.128.1') def test_jenkins_upgradable_without_bundle_site(self): """ Jenkins should always be upgradable when bundle-site isn't set. """ self.assertTrue(self.packages.jenkins_upgradable()) self.apt._set_jenkins_version('2.128.1') self.packages._jc.core_version = '2.128.1' self.assertTrue(self.packages.jenkins_upgradable()) @mock.patch("charms.layer.jenkins.packages.JenkinsCore") def test_jenkins_upgradable_with_bundle_site(self, mock_jenkins_core_version): """ If the latest jenkins package version available in bundle-site is higher than the installed one, jenkins will be upgradable. """ orig_bundle_site = hookenv.config()["bundle-site"] try: hookenv.config()["bundle-site"] = "http://test" self.packages = Packages(apt=self.apt, ch_host=self.ch_host) self.apt._set_jenkins_version('2.128.1') self.packages._jc.core_version = '2.128.2' self.assertTrue(self.packages.jenkins_upgradable()) self.packages._jc.core_version = '2.128.1' self.assertFalse(self.packages.jenkins_upgradable()) finally: hookenv.config()["bundle-site"] = orig_bundle_site def test_bundle_download(self): bundle_path = os.path.join(hookenv.charm_dir(), "jenkins.deb") self.packages._bundle_download(bundle_path) self.assertThat(bundle_path, PathExists()) def test_install_jenkins_bundle_download(self): """ If the 'release' config is set to 'bundle' and bundle-site is set, then Jenkins will be downloaded and installed from bundle-site. """ orig_release = hookenv.config()["release"] orig_bundle_site = hookenv.config()["bundle-site"] try: hookenv.config()["release"] = "bundle" hookenv.config()["bundle-site"] = "https://pkg.jenkins.io" bundle_path = os.path.join(hookenv.charm_dir(), "files") self.packages.install_jenkins() self.assertTrue(glob('%s/jenkins_*.deb' % bundle_path)) self.assertEqual( ["install"], self.fakes.processes.dpkg.actions["jenkins"]) finally: hookenv.config()["release"] = orig_release hookenv.config()["bundle-site"] = orig_bundle_site @mock.patch("charms.layer.jenkins.packages.os.system") def test_clean_old_plugins(self, mock_os_system): """Make sure old plugin directories are excluded.""" plugins = ["test1_plugin", "test2_plugin", "test3_plugin"] kept_plugins = [] detached_plugins_dir = "/var/cache/jenkins/war/WEB-INF/detached-plugins" os_expected_calls = [mock.call("sudo rm -r %s" % detached_plugins_dir)] os.mkdir(detached_plugins_dir) for plugin in plugins: # Create old plugins directories and .jpi files with no version plugin_dir = os.path.join(paths.PLUGINS, plugin) plugin_file = os.path.join(paths.PLUGINS, "%s.jpi" % plugin) os.mkdir(plugin_dir) with open(plugin_file, "w") as fd: fd.write("") # And expect them to be removed os_expected_calls.append(mock.call("sudo rm -r %s/" % plugin_dir)) os_expected_calls.append(mock.call("sudo rm %s" % plugin_file)) # Create plugins with version that should not be removed plugin_to_keep = os.path.join(paths.PLUGINS, "%s-1.jpi" % plugin) kept_plugins.append(plugin_to_keep) with open(plugin_to_keep, "w") as fd: fd.write("") self.packages.clean_old_plugins() self.assertThat(paths.PLUGINS, PathExists()) self.assertCountEqual(mock_os_system.mock_calls, os_expected_calls) mock_os_system.assert_has_calls(os_expected_calls, any_order=True)
def setUp(self): super(PackagesTest, self).setUp() self.apt = AptStub() self.packages = Packages(apt=self.apt)
class PackagesTest(CharmTest): def setUp(self): super(PackagesTest, self).setUp() self.apt = AptStub() self.ch_host = CharmHelpersCoreHostStub() self.packages = Packages(apt=self.apt, ch_host=self.ch_host) # XXX Not all charm files are populated in charm_dir() by default. # XXX See: https://github.com/freeekanayaka/charm-test/issues/2 keyfile = "jenkins.io.key" os.symlink(os.path.join(os.getcwd(), keyfile), os.path.join(hookenv.charm_dir(), keyfile)) def tearDown(self): # Reset installs and sources after each test super(PackagesTest, self).tearDown() self.packages.installs = [] self.packages.sources = [] def test_install_dependencies(self): """ The Jenkins dependencies get installed by the install_dependencies method. """ # Our default distro version (xenial). self.assertEqual(self.packages.distro_codename(), 'xenial') self.packages.install_dependencies() self.assertEqual(APT_DEPENDENCIES['xenial'], self.apt.installs) # Now check with a distro of bionic. self.apt.installs = [] self.ch_host._set_distro_version('bionic') self.assertEqual(self.packages.distro_codename(), 'bionic') self.packages.install_dependencies() self.assertEqual(APT_DEPENDENCIES['bionic'], self.apt.installs) def test_install_tools(self): """ The requested tools get installed by the install_tools method. """ orig_tools = hookenv.config()["tools"] try: hookenv.config()["tools"] = "git gcc" self.packages.install_tools() self.assertEqual(["git", "gcc"], self.apt.installs) finally: hookenv.config()["tools"] = orig_tools def test_install_jenkins_bundle(self): """ If the 'release' config is set to 'bundle', then Jenkins will be installed from a local jenkins.deb file. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "bundle" files = os.path.join(hookenv.charm_dir(), "files") os.mkdir(files) bundle_path = os.path.join(files, "jenkins.deb") with open(bundle_path, "w") as fd: fd.write("") self.packages.install_jenkins() self.assertEqual(["install"], self.fakes.processes.dpkg.actions["jenkins"]) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_bundle_no_file(self): """ If the 'release' config is set to 'bundle' but no jenkins.deb file is present, an error is raised. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "bundle" error = self.assertRaises(Exception, self.packages.install_jenkins) path = os.path.join(hookenv.charm_dir(), "files", "jenkins.deb") self.assertEqual( "'{}' doesn't exist. No package bundled.".format(path), str(error)) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_remote(self): """ If the 'release' config is set to a remote URL, then Jenkins will be installed from the deb files pointed by that url. """ self.fakes.processes.wget.locations[ "http://jenkins-1.2.3.deb"] = b"data" orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "http://jenkins-1.2.3.deb" self.packages.install_jenkins() self.assertEqual(["install"], self.fakes.processes.dpkg.actions["jenkins"]) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_lts_release(self): """ If the 'release' config is set to 'lts', an APT source entry will be added, pointing to the debian-stable Jenkins repository. """ self.packages.install_jenkins() source = APT_SOURCE % "debian-stable" key = os.path.join(hookenv.charm_dir(), "jenkins.io.key") with open(key, "r") as k: key = k.read() self.assertEqual([(source, key)], self.apt.sources) def test_install_jenkins_trunk_release(self): """ If the 'release' config is set to 'trunk', an APT source entry will be added, pointing to the debian Jenkins repository. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "trunk" self.packages.install_jenkins() source = APT_SOURCE % "debian" key = os.path.join(hookenv.charm_dir(), "jenkins.io.key") with open(key, "r") as k: key = k.read() self.assertEqual([(source, key)], self.apt.sources) finally: hookenv.config()["release"] = orig_release def test_install_jenkins_invalid_release(self): """ If the 'release' config is invalid, an error is raised. """ orig_release = hookenv.config()["release"] try: hookenv.config()["release"] = "foo" error = self.assertRaises(Exception, self.packages.install_jenkins) self.assertEqual("Release 'foo' configuration not recognised", str(error)) finally: hookenv.config()["release"] = orig_release def test_jenkins_version(self): self.assertEqual(self.packages.jenkins_version(), '2.150.1') # And now test older version. self.apt._set_jenkins_version('2.128.1') self.assertEqual(self.packages.jenkins_version(), '2.128.1')