From d791eea9940a8e655b38fb80983f8355fca13f71 Mon Sep 17 00:00:00 2001 From: David Andersson <51036209+jdkandersson@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:15:41 +1000 Subject: [PATCH] Upgrade Jenkins to 2.245.2 (#171) * Upgrade Jenkins to 2.245.2 * chore: ignore pebble trivy warning * debug * test: wait jenkins ready before test * test: wait jenkins ready before test * test: use fixtures * test: revert wait_jenkins_ready fixture * test: fix lint * test: debug * test: fix timeout errors & groovy pipeline plugin string match * test: fix lint * test: debug * test: increase timeout * test: wait and block till active * test: fix cloud descriptor name * test: revert debug * chore: remove patched CVEs in trivyignore * chore: remove vscode artefact * chore: add pebble CVEs to trivyignore * chore: update CVEs * chore: add concurrency to tests --------- Co-authored-by: Yanks Yoon --- .github/workflows/integration_test.yaml | 4 +++ .trivyignore | 16 ++++------ jenkins_rock/rockcraft.yaml | 2 +- tests/integration/conftest.py | 10 +++++- tests/integration/helpers.py | 22 ++++++------- tests/integration/requirements.txt | 1 + tests/integration/test_plugins.py | 41 +++++++++++++++++++------ 7 files changed, 61 insertions(+), 35 deletions(-) diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index 96c2a500..d0ada1b5 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -3,6 +3,10 @@ name: Integration tests on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: integration-tests: uses: canonical/operator-workflows/.github/workflows/integration_test.yaml@main diff --git a/.trivyignore b/.trivyignore index 76ac5495..672b0cdf 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,11 +1,7 @@ -# Fixed in 5.3.33 -CVE-2024-22259 -# Fixed in 5.7.12 -CVE-2024-22257 -CVE-2024-22262 -# Jenkins Plugin Manager CVEs -CVE-2016-1000027 -CVE-2023-5072 -CVE-2024-23898 -# Other +# Pebble CVEs +CVE-2024-24790 CVE-2023-45288 +# Jenkins plugin manager CVEs +CVE-2023-5072 +# Jenkins CVEs +CVE-2016-1000027 diff --git a/jenkins_rock/rockcraft.yaml b/jenkins_rock/rockcraft.yaml index 57dbfa50..566bf6aa 100644 --- a/jenkins_rock/rockcraft.yaml +++ b/jenkins_rock/rockcraft.yaml @@ -45,7 +45,7 @@ parts: - default-jre-headless - git build-environment: - - JENKINS_VERSION: 2.452.1 + - JENKINS_VERSION: 2.452.2 - JENKINS_PLUGIN_MANAGER_VERSION: 2.12.13 override-build: | mkdir -p ${CRAFT_PART_INSTALL}/{srv/jenkins/,etc/default/jenkins/} diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 87315714..368f8189 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -15,6 +15,7 @@ import kubernetes.stream import pytest import pytest_asyncio +import requests from juju.action import Action from juju.application import Application from juju.client._definitions import FullStatus, UnitStatus @@ -519,10 +520,17 @@ async def jenkins_with_proxy_client_fixture( @pytest_asyncio.fixture(scope="function", name="app_with_allowed_plugins") async def app_with_allowed_plugins_fixture( - application: Application, + application: Application, web_address: str ) -> AsyncGenerator[Application, None]: """Jenkins charm with plugins configured.""" await application.set_config({"allowed-plugins": ",".join(ALLOWED_PLUGINS)}) + model: Model = application.model + await model.wait_for_idle(apps=[application.name], wait_for_active=True) + await model.block_until( + lambda: requests.get(web_address, timeout=10).status_code == 403, + timeout=60 * 10, + wait_period=10, + ) yield application await application.reset_config(to_default=["allowed-plugins"]) diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 490cf7ac..3a3ffced 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -12,6 +12,7 @@ import jenkinsapi.jenkins import kubernetes.client import requests +import tenacity from juju.application import Application from juju.model import Model from juju.unit import Unit @@ -24,6 +25,11 @@ logger = logging.getLogger(__name__) +@tenacity.retry( + wait=tenacity.wait_exponential(multiplier=2, max=60), + reraise=True, + stop=tenacity.stop_after_attempt(5), +) async def install_plugins( unit_web_client: UnitWebClient, plugins: typing.Iterable[str], @@ -450,18 +456,8 @@ def create_secret_file_credentials( }}, }}""" } - - accept_header = ( - "text/html," - "application/xhtml+xml," - "application/xml;q=0.9," - "image/avif,image/webp," - "image/apng," - "*/*;q=0.8," - "application/signed-exchange;v=b3;q=0.9'" - ) headers = { - "Accept": accept_header, + "Accept": "*/*", } with open(kube_config, "rb") as kube_config_file: @@ -493,7 +489,7 @@ def create_kubernetes_cloud( payload = { "name": kubernetes_test_cloud_name, - "type": "org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud", + "cloudDescriptorName": "org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud", "json": f""" {{ "name": "{kubernetes_test_cloud_name}", @@ -522,7 +518,7 @@ def create_kubernetes_cloud( logger.debug("Creating jenkins kubernets cloud, params: %s %s", headers, payload) res = unit_web_client.client.requester.post_url( - url=url, headers=headers, data=payload, timeout=30 + url=url, headers=headers, data=payload, timeout=60 * 5 ) logger.debug("Cloud created, %s", res.status_code) diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt index 8416585e..01677e96 100644 --- a/tests/integration/requirements.txt +++ b/tests/integration/requirements.txt @@ -2,3 +2,4 @@ Jinja2>=3,<4 lightkube==0.15.1 pytest-playwright==0.4.4 python-keycloak>=3,<4 +tenacity==8.4.2 diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py index 461dccd0..cf1cef8d 100644 --- a/tests/integration/test_plugins.py +++ b/tests/integration/test_plugins.py @@ -3,6 +3,7 @@ """Integration tests for jenkins-k8s-operator charm.""" +import functools import json import logging import typing @@ -10,6 +11,7 @@ import jenkinsapi.plugin import pytest import requests +import urllib3.exceptions from jinja2 import Environment, FileSystemLoader from juju.application import Application from pytest_operator.plugin import OpsTest @@ -39,14 +41,27 @@ async def test_plugins_remove_delay( """ arrange: given a Jenkins with plugins being installed through UI. act: when update_status_hook is fired. - assert: the plugin removal is delayed warning is logged until plugin installation is settled. + assert: the plugin removal delayed warning is logged until plugin installation is settled. """ post_data = {f"plugin.{plugin}.default": "on" for plugin in ALLOWED_PLUGINS} post_data["dynamic_load"] = "" - res = unit_web_client.client.requester.post_url( - f"{unit_web_client.web}/manage/pluginManager/install", data=post_data - ) - assert res.status_code == 200, "Failed to request plugins install" + + def _install_plugins_via_web_api() -> bool: + """Install plugins via pluginManager API. + + Returns: + Whether the plugin installation request has succeeded. + """ + try: + res = unit_web_client.client.requester.post_url( + f"{unit_web_client.web}/manage/pluginManager/install", data=post_data + ) + return res.ok + except (requests.exceptions.RequestException, urllib3.exceptions.HTTPError): + logger.exception("Failed to post plugin installations.") + return False + + await wait_for(_install_plugins_via_web_api) async def has_temp_files(): """Check if tempfiles exist in Jenkins plugins directory. @@ -458,8 +473,10 @@ async def test_groovy_libs_plugin(unit_web_client: UnitWebClient): res = unit_web_client.client.requester.get_url(f"{unit_web_client.web}/manage/configure") config_page = str(res.content, "utf-8") + # The string is now "Global Trusted Pipeline Libraries" and + # "Global Untrusted Pipeline Libraries" for v727.ve832a_9244dfa_ assert ( - "Global Pipeline Libraries" in config_page + "Pipeline Libraries" in config_page ), f"Groovy libs configuration option not found. {config_page}" @@ -580,10 +597,14 @@ async def test_kubernetes_plugin(unit_web_client: UnitWebClient, kube_config: st """ # Use plain credentials to be able to create secret-file/secret-text credentials await install_plugins(unit_web_client, ("kubernetes", "plain-credentials")) - credentials_id = create_secret_file_credentials(unit_web_client, kube_config) - assert credentials_id - kubernetes_cloud_name = create_kubernetes_cloud(unit_web_client, credentials_id) - assert kubernetes_cloud_name + credentials_id = await wait_for( + functools.partial(create_secret_file_credentials, unit_web_client, kube_config) + ) + assert credentials_id, "Failed to create credentials id" + kubernetes_cloud_name = await wait_for( + functools.partial(create_kubernetes_cloud, unit_web_client, credentials_id) + ) + assert kubernetes_cloud_name, "Failed to create kubernetes cloud" job = unit_web_client.client.create_job( "kubernetes_plugin_test", gen_test_pipeline_with_custom_script_xml(kubernetes_test_pipeline_script()),