예제 #1
0
    def configure_admin(self):
        """Configure the admin user."""
        hookenv.log("Configuring user for jenkins")

        admin = self._admin_data()
        api = Api(packages=self._packages)
        api.update_password(admin.username, admin.password)

        # Save the password to a file. It's not used directly by this charm
        # but it's convenient for integration with third-party tools.
        host.write_file(paths.ADMIN_PASSWORD,
                        admin.password.encode("utf-8"),
                        owner="root",
                        group="root",
                        perms=0o0600)

        if not os.path.exists(paths.LAST_EXEC):
            # This mean it's the very first time we configure the user,
            # and we want to create this file in order to avoid Jenkins
            # presenting the setup wizard.
            host.write_file(paths.LAST_EXEC,
                            "{}\n".format(api.version()).encode("utf-8"),
                            owner="jenkins",
                            group="nogroup",
                            perms=0o0600)
예제 #2
0
def configure_admin():
    remove_state("jenkins.configured.admin")
    api = Api()

    status_set("maintenance", "Configuring Jenkins public url")
    configuration = Configuration()
    needs_restart = configuration.set_url()
    if needs_restart:
        status_set("maintenance", "Restarting Jenkins")
        service_restart('jenkins')
        api.wait()

    status_set("maintenance", "Configuring proxy settings")
    configuration.configure_proxy()
    service_restart('jenkins')
    api.wait()

    status_set("maintenance", "Configuring admin user")
    users = Users()
    users.configure_admin()

    api.reload()
    api.wait()  # Wait for the service to be fully up
    # Inform any extension that the username/password changed
    if get_state("extension.connected"):
        extension_relation = (RelationBase.from_state("extension.connected"))
        extension_relation.joined()

    set_state("jenkins.configured.admin")
예제 #3
0
def configure_admin():
    remove_state("jenkins.configured.admin")
    status_set("maintenance", "Configuring admin user")
    users = Users()
    users.configure_admin()
    api = Api()
    api.reload()
    api.wait()  # Wait for the service to be fully up
    set_state("jenkins.configured.admin")
예제 #4
0
def add_slaves(master):
    slaves = master.slaves()
    if not data_changed("master.slaves", slaves):
        log("Slaves are unchanged - no need to do anything")
        return
    api = Api()
    for slave in slaves:
        api.add_node(
            slave["slavehost"], slave["executors"],
            labels=slave["labels"] or ())
예제 #5
0
def add_slaves(master):
    slaves = master.slaves()
    if not data_changed("master.slaves", slaves):
        log("Slaves are unchanged - no need to do anything")
        return
    api = Api()
    for slave in slaves:
        api.add_node(slave["slavehost"],
                     slave["executors"],
                     labels=slave["labels"] or ())
예제 #6
0
 def departed(self):
     """Indicate the relation is no longer available and not connected."""
     # Slave hostname is derived from unit name so
     # this is pretty safe
     slavehost = remote_unit()
     log("Deleting slave with hostname %s." % slavehost)
     api = Api()
     api.delete_node(slavehost.replace("/", "-"))
     self.remove_state("{relation_name}.available")
     self.remove_state("{relation_name}.connected")
예제 #7
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)
    def changed(self):
        """Install optional plugins."""
        # extension subordinates may request the principle service install
        # specified jenkins plugins
        log("Installing required plugins as requested by jenkins-extension "
            "subordinate.")
        plugins = Plugins()
        plugins.install(relation_get("required_plugins"))

        api = Api()
        api.wait()  # Wait for the service to be fully up
예제 #9
0
    def changed(self):
        """Install optional plugins."""
        # extension subordinates may request the principle service install
        # specified jenkins plugins
        log("Installing required plugins as requested by jenkins-extension "
            "subordinate.")
        plugins = Plugins()
        plugins.install(relation_get("required_plugins"))

        api = Api()
        api.wait()  # Wait for the service to be fully up
예제 #10
0
    def broken(self):
        """Indicate the relation is no longer available and not connected."""
        api = Api()
        for member in relation_ids():
            member = member.replace("/", "-")
            log("Removing node %s from Jenkins master." % member)
            api.delete_node(member)

        self.remove_state("{relation_name}.available")
        self.remove_state("{relation_name}.connected")
        self.remove_state("{relation_name}.tls.available")
예제 #11
0
def configure_plugins():
    if get_state("extension.connected"):
        # We've been driven by an extension, let it take control over
        # plugin.
        log("External relation detected - skip configuring plugins")
        return
    status_set("maintenance", "Configuring plugins")
    remove_state("jenkins.configured.plugins")
    plugins = Plugins()
    plugins.install(config("plugins"))
    api = Api()
    api.wait()  # Wait for the service to be fully up
    set_state("jenkins.configured.plugins")
예제 #12
0
def configure_plugins():
    if get_state("extension.connected"):
        # We've been driven by an extension, let it take control over
        # plugin.
        log("External relation detected - skip configuring plugins")
        return
    status_set("maintenance", "Configuring plugins")
    remove_state("jenkins.configured.plugins")
    plugins = Plugins()
    plugins.install(config("plugins"))
    api = Api()
    api.wait()  # Wait for the service to be fully up
    set_state("jenkins.configured.plugins")
예제 #13
0
def upgrade_jenkins():
    if config("release") == "bundle":
        packages = Packages()
        if packages.jenkins_upgradable():
            status_set("maintenance", "Upgrading Jenkins")
            packages.install_jenkins()
            api = Api()
            api.wait()  # Wait for the upgrade to finish
            packages.clean_old_plugins()
            unitdata.kv().set("jenkins.plugins.last_update", 0)
            update_plugins()
        else:
            log("No newer jenkins package is available")
예제 #14
0
def update_plugins():
    last_update = unitdata.kv().get("jenkins.plugins.last_update")
    if last_update is None:
        unitdata.kv().set("jenkins.plugins.last_update", 0)
        last_update = 0
    # Only try to update plugins when the interval configured has passed
    update_interval = time.time() - (config("plugins-auto-update-interval") * 60)
    if (last_update < update_interval):
        status_set("maintenance", "Updating plugins")
        plugins = Plugins()
        plugins.update(config("plugins"))
        api = Api()
        api.wait()  # Wait for the service to be fully up
    unitdata.kv().set("jenkins.plugins.last_update", time.time())
예제 #15
0
    def install(self, plugins):
        """Install the given plugins, optionally removing unlisted ones.

        @params plugins: A whitespace-separated list of plugins to install.
        """
        hookenv.log("Starting plugins installation process")
        plugins = plugins or ""
        plugins = plugins.split()
        plugins = self._get_plugins_to_install(plugins)

        host.mkdir(paths.PLUGINS,
                   owner="jenkins",
                   group="jenkins",
                   perms=0o0755)
        existing_plugins = set(glob.glob("%s/*.jpi" % paths.PLUGINS))
        try:
            installed_plugins = self._install_plugins(plugins)
        except Exception:
            hookenv.log("Plugin installation failed, check logs for details")
            raise

        unlisted_plugins = existing_plugins - installed_plugins
        if unlisted_plugins:
            if hookenv.config()["remove-unlisted-plugins"] == "yes":
                self._remove_plugins(unlisted_plugins)
            else:
                hookenv.log("Unlisted plugins: (%s) Not removed. Set "
                            "remove-unlisted-plugins to 'yes' to clear them "
                            "away." % ", ".join(unlisted_plugins))

        # Restarting jenkins to pickup configuration changes
        Api().restart()
        return installed_plugins
예제 #16
0
 def test_check_ready_unavailable(self):
     """
     If the backend keeps returning 5xx, an error is raised.
     """
     api = Api()
     self.fakes.network.get(api.url, status_code=500)
     self.assertRaises(ServiceUnavailable, self.service.check_ready)
예제 #17
0
    def configure_proxy(self):
        """Check whether the machine is configured to use an http(s) proxy
        and if it does - propagate the environment proxy settings to Jenkins."""
        env_proxy = (os.environ['HTTP_PROXY'] or os.environ['HTTPS_PROXY']
                     or os.environ['http_proxy'] or os.environ['https_proxy'])

        if not env_proxy:
            hookenv.log("There are no environment proxy settings")
            return

        hookenv.log("There are environment proxy settings")
        url = urlparse(env_proxy)

        noproxy = os.environ['NO_PROXY'] or os.environ['no_proxy']
        if noproxy:
            noproxy = ' '.join(
                [re.sub("^\.", '*.', x.strip()) for x in noproxy.split(',')])

        api = Api()
        api.configure_proxy(url.hostname, url.port, url.username, url.password,
                            noproxy)
예제 #18
0
    def configure_admin(self):
        """Configure the admin user."""
        hookenv.log("Configuring user for jenkins")

        admin = self._admin_data()
        api = Api()
        api.update_password(admin.username, admin.password)

        # Save the password to a file. It's not used directly by this charm
        # but it's convenient for integration with third-party tools.
        host.write_file(
            paths.ADMIN_PASSWORD, admin.password.encode("utf-8"),
            owner="root", group="root", perms=0o0600)

        if not os.path.exists(paths.LAST_EXEC):
            # This mean it's the very first time we configure the user,
            # and we want to create this file in order to avoid Jenkins
            # presenting the setup wizard.
            host.write_file(
                paths.LAST_EXEC, "{}\n".format(api.version()).encode("utf-8"),
                owner="jenkins", group="nogroup")
예제 #19
0
 def _install_plugin(self, plugin, plugins_site, update):
     """
     Verify if the plugin is not installed before installing it
     or if it needs an update .
     """
     plugin_version = Api().get_plugin_version(plugin)
     latest_version = self._get_latest_version(plugin)
     if not plugin_version or (update and plugin_version != latest_version):
         hookenv.log("Installing plugin %s-%s" % (plugin, latest_version))
         plugin_url = ("%s/%s.hpi" % (plugins_site, plugin))
         return self._download_plugin(plugin, plugin_url)
     hookenv.log("Plugin %s-%s already installed" %
                 (plugin, plugin_version))
예제 #20
0
def update_nrpe_config(nagios):
    unit_data = unitdata.kv()
    nagios_hostname = unit_data.get('nagios.hostname', None)
    nagios_host_context = unit_data.get('nagios.host_context', None)

    # require the nrpe-external-master relation to provide the host context
    if in_relation_hook() and relation_id().\
            startswith('nrpe-external-master:'):
        rel = relation_get()
        if 'nagios_host_context' in rel:
            nagios_host_context = rel['nagios_host_context']
            unit_data.set('nagios.host_context', nagios_host_context)

            # We have to strip the nagios host context from the nagios hostname
            # since the nagios.add_check will put it back again...
            nagios_hostname = rel['nagios_hostname']
            if nagios_hostname.startswith(nagios_host_context + '-'):
                nagios_hostname = nagios_hostname[len(nagios_host_context +
                                                      '-'):]

            unit_data.set('nagios.hostname', nagios_hostname)

    if not nagios_hostname or not nagios_host_context:
        return
    # The above boilerplate is needed until this issue is fixed:
    #
    # https://github.com/cmars/nrpe-external-master-interface/issues/6

    status_set('maintenance', 'Updating Nagios configs')

    creds = Credentials()
    check = [
        '/usr/lib/nagios/plugins/check_http',
        '-H',
        'localhost',
        '-p',
        '8080',
        '-u',
        urlparse(Api().url).path,
        '-a',
        "{}:{}".format(creds.username(), creds.token()),
    ]
    nagios.add_check(check,
                     name="check_jenkins_http",
                     description="Verify Jenkins HTTP is up.",
                     context=nagios_host_context,
                     unit=nagios_hostname)

    status_set('active', 'Ready')
예제 #21
0
def update_plugins():
    last_update = unitdata.kv().get("jenkins.plugins.last_update")
    if last_update is None:
        unitdata.kv().set("jenkins.plugins.last_update", 0)
        last_update = 0
    # Only try to update plugins when the interval configured has passed
    update_interval = time.time() - (config("plugins-auto-update-interval") *
                                     60)
    if (last_update < update_interval):
        status_set("maintenance", "Updating plugins")
        remove_state("jenkins.updated.plugins")
        plugins = Plugins()
        plugins.update(config("plugins"))
        api = Api()
        api.wait()  # Wait for the service to be fully up
        # Restart jenkins if any plugin got updated
        last_restart = unitdata.kv().get("jenkins.last_restart") or 0
        last_plugin_update_time = (
            unitdata.kv().get("jenkins.plugins.last_plugin_update_time") or 0)
        if (last_restart < last_plugin_update_time):
            restart()
        unitdata.kv().set("jenkins.plugins.last_restart", time.time())
        set_state("jenkins.updated.plugins")
    unitdata.kv().set("jenkins.plugins.last_update", time.time())
예제 #22
0
    def test_check_ready_transient_failure(self):
        """
        Transient failures are retried.
        """
        start = time.time()

        def callback(requests, context):
            if time.time() - start >= 2:
                context.status_code = 503
            else:
                context.status_code = 200
            return ""

        api = Api()
        self.fakes.network.get(api.url, text=callback)
        self.assertIsNone(self.service.check_ready())
예제 #23
0
    def _setUp(self):
        self.fakes.users.add("jenkins", 123)
        self.fakes.groups.add("nogroup", 456)

        self.fakes.fs.add(paths.HOME)
        os.makedirs(paths.SECRETS)
        with open(paths.INITIAL_PASSWORD, "w") as fd:
            fd.write(INITIAL_PASSWORD)

        self.fakes.fs.add(paths.DEFAULTS_CONFIG_FILE)
        os.makedirs('/etc/default')
        with open(paths.DEFAULTS_CONFIG_FILE, "wb") as fd:
            fd.write(b"# port for HTTP connector\nHTTP_PORT=8080\n")
            fd.write(b'JENKINS_ARGS="--httpPort=$HTTP_PORT"')

        api = Api()
        self.fakes.network.get(api.url, headers={"X-Jenkins": "2.0.0"})
예제 #24
0
def configure_admin():
    remove_state("jenkins.configured.admin")
    status_set("maintenance", "Configuring admin user")
    users = Users()
    users.configure_admin()
    api = Api()
    api.reload()
    api.wait()  # Wait for the service to be fully up
    # Inform any extension that the username/password changed
    if get_state("extension.connected"):
        extension_relation = (RelationBase.from_state("extension.connected"))
        extension_relation.joined()

    set_state("jenkins.configured.admin")
예제 #25
0
    def update(self, plugins):
        """Try to update the given plugins.

        @params plugins: A whitespace-separated list of plugins to install.
        """
        plugins = plugins or ""
        plugins = plugins.split()
        plugins = self._get_plugins_to_install(plugins)
        hookenv.log("Updating plugins")
        try:
            installed_plugins = self._install_plugins(plugins)
        except Exception:
            hookenv.log("Plugin update failed, check logs for details")
            raise

        if len(installed_plugins) == 0:
            hookenv.log("No plugins updated")
            return
        else:
            Api().restart()
            return installed_plugins
예제 #26
0
 def check_ready(self):
     """Build a Jenkins client instance."""
     api = Api()
     response = requests.get(api.url)
     if response.status_code >= 500:
         raise ServiceUnavailable()
예제 #27
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
예제 #28
0
 def setUp(self):
     super(ApiTest, self).setUp()
     self.useFixture(JenkinsConfiguredAdmin(self.fakes))
     self.fakes.jenkins.scripts[GET_TOKEN_SCRIPT.format("admin")] = "abc\n"
     self.api = Api()
예제 #29
0
def restart():
    api = Api()
    api.restart()
    api.wait()  # Wait for the service to be fully up
    unitdata.kv().set("jenkins.last_restart", time.time())
예제 #30
0
 def setUp(self):
     super(ApiTest, self).setUp()
     self.useFixture(JenkinsConfiguredAdmin(self.fakes))
     self.fakes.jenkins.scripts[GET_TOKEN_SCRIPT.format("admin")] = "abc\n"
     self.api = Api()
예제 #31
0
class ApiTest(JenkinsTest):
    def setUp(self):
        super(ApiTest, self).setUp()
        self.useFixture(JenkinsConfiguredAdmin(self.fakes))
        self.fakes.jenkins.scripts[GET_TOKEN_SCRIPT.format("admin")] = "abc\n"
        self.api = Api()

    def test_wait_transient_failure(self):
        """
        Wait for Jenkins to be fully up, even in spite of transient failures.
        """
        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.
        """
        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.assertEqual("2.0.0", self.api.version())

    def test_add(self):
        """
        A slave node can be added by specifying executors and labels.
        """
        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)

    def test_add_exists(self):
        """
        If a node already exists, nothing is done.
        """
        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.
        """
        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.
        """
        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.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.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.api.delete_node("slave-0")
        self.assertEqual([], self.fakes.jenkins.nodes)

    def test_reload(self):
        """
        The reload method POSTs a request to the '/reload' URL, expecting
        a 503 on the homepage (which happens after redirection).
        """
        error = HTTPError(URL, 503, "Service Unavailable", {}, None)
        error.url = URL
        self.fakes.jenkins.responses[urljoin(URL, "/reload")] = error
        self.api.reload()

    def test_reload_unexpected_error(self):
        """
        If the error code is not 403, the error is propagated.
        """
        error = HTTPError(URL, 403, "Forbidden", {}, None)
        self.fakes.jenkins.responses[urljoin(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.
        """
        error = HTTPError(URL, 503, "Service Unavailable", {}, None)
        error.url = "/foo"
        self.fakes.jenkins.responses[urljoin(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.fakes.jenkins.responses[urljoin(URL, "/reload")] = "home"
        self.assertRaises(RuntimeError, self.api.reload)
예제 #32
0
class ApiTest(JenkinsTest):

    def setUp(self):
        super(ApiTest, self).setUp()
        self.useFixture(JenkinsConfiguredAdmin(self.fakes))
        self.fakes.jenkins.scripts[GET_TOKEN_SCRIPT.format("admin")] = "abc\n"
        self.api = Api()

    def test_wait_transient_failure(self):
        """
        Wait for Jenkins to be fully up, even in spite of transient failures.
        """
        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.
        """
        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.assertEqual("2.0.0", self.api.version())

    def test_add(self):
        """
        A slave node can be added by specifying executors and labels.
        """
        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)

    def test_add_exists(self):
        """
        If a node already exists, nothing is done.
        """
        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.
        """
        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.
        """

        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.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.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.api.delete_node("slave-0")
        self.assertEqual([], self.fakes.jenkins.nodes)

    def test_reload(self):
        """
        The reload method POSTs a request to the '/reload' URL, expecting
        a 503 on the homepage (which happens after redirection).
        """
        error = HTTPError(URL, 503, "Service Unavailable", {}, None)
        error.url = URL
        self.fakes.jenkins.responses[urljoin(URL, "/reload")] = error
        self.api.reload()

    def test_reload_unexpected_error(self):
        """
        If the error code is not 403, the error is propagated.
        """
        error = HTTPError(URL, 403, "Forbidden", {}, None)
        self.fakes.jenkins.responses[urljoin(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.
        """
        error = HTTPError(URL, 503, "Service Unavailable", {}, None)
        error.url = "/foo"
        self.fakes.jenkins.responses[urljoin(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.fakes.jenkins.responses[urljoin(URL, "/reload")] = "home"
        self.assertRaises(RuntimeError, self.api.reload)