예제 #1
0
 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)
예제 #2
0
 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)
예제 #3
0
 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))
예제 #4
0
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"])
예제 #5
0
    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)
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
 def setUp(self):
     super(PackagesTest, self).setUp()
     self.apt = AptStub()
     self.packages = Packages(apt=self.apt)
예제 #9
0
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')