Skip to content

Support juju 4 in integration tests - #596

Open
Thanhphan1147 wants to merge 20 commits into
mainfrom
migrate-integration-tests-juju4
Open

Support juju 4 in integration tests#596
Thanhphan1147 wants to merge 20 commits into
mainfrom
migrate-integration-tests-juju4

Conversation

@Thanhphan1147

@Thanhphan1147 Thanhphan1147 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

  • Replaces all juju.ssh(unit, cmd) / cli("ssh", ...) calls that read files on units with juju.exec(cmd, unit=...).stdout.
  • Stabilize TLS, route, policy, config, and legacy tests with more aggressive juju.wait
  • Run OAuth SPOE coverage separately on Juju 3 due to an upstream Juju 4 Kubernetes image issue.
  • Update relation addresses to use ingress-address.

juju ssh fails on Juju 4 with 'Permission denied (publickey)' due to
changed SSH key provisioning timing. Replace all ssh calls that read
files on units with juju.exec(..., unit=...).stdout, which routes
through the Juju agent and requires no SSH keypair.

Affected tests: test_haproxy_route, test_haproxy_route_tcp (x4),
test_haproxy_ddos, and the apache2 log helper.
@Thanhphan1147
Thanhphan1147 requested a review from a team as a code owner July 2, 2026 13:18
@Thanhphan1147 Thanhphan1147 changed the title test: replace juju ssh with juju exec to fix Juju 4 SSH key auth failures Support juju 4 in integration tests Jul 2, 2026
Dependency charms (postgresql 16/edge, postgresql-k8s 14/edge, hydra,
kratos, traefik-k8s, identity-platform-login-ui-operator,
self-signed-certificates) declare 'assumes: juju < 4.0.0' (or < 3.5.0),
blocking deployment on Juju 4.

Juju 4's deploy validator skips assumes checks when --force is passed.
Add force=True to all external charm deploys in test fixtures so
the assumes gate is bypassed without changing the charm revisions.

Affected fixtures: postgresql_fixture, deploy_iam_bundle_fixture,
certificate_provider_application_fixture (both top-level and
haproxy-operator conftest).
Comment on lines +31 to +38
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, haproxy_route_requirer
)
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, haproxy_route_requirer
)
),
)
juju.wait(
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, haproxy_route_requirer
)
),
)

Comment on lines +340 to +347
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_tcp_requirer
)
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_tcp_requirer
)
),
)
juju.wait(
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_tcp_requirer
)
),
)

Comment on lines +86 to +101

deadline = time.monotonic() + 300
hashed_tcp_client = None
while time.monotonic() < deadline:
with (
socket.create_connection((str(haproxy_ip_address), 4444), timeout=30) as sock,
context.wrap_socket(sock, server_hostname="example.com") as secure_socket,
):
secure_socket.sendall(b"ping")
assert b"pong" in secure_socket.read()
field = _last_client_field(juju, unit, "haproxy_route_tcp_4444", required=False)
if field is not None and not _is_plaintext_address(field):
hashed_tcp_client = field
break
time.sleep(5)
assert hashed_tcp_client is not None, "no hashed client field found in haproxy TCP logs"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit
use tenacity

Comment on lines +106 to +112
def _is_plaintext_address(field: str) -> bool:
"""Return whether a logged client field is a plaintext IP:port entry."""
try:
ip_address(field.rsplit(":", 1)[0])
except ValueError:
return False
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if relevant
but should we validate the port?
i assume something like 127.0.0.1: would pass validation

Comment on lines +142 to +154
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
marker = f"{marker_prefix}-{uuid.uuid4().hex}"
response = requests.get(f"{address}/?{marker}", verify=False, timeout=30) # nosec
assert response.status_code == 200
field = _last_client_field(juju, unit, marker, required=False)
if field is not None and _is_plaintext_address(field) == expect_plaintext:
return field
time.sleep(5)
raise TimeoutError(
f"no {'plaintext' if expect_plaintext else 'hashed'} client field found"
f" in haproxy logs within {timeout}s"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit
tenacity

Comment on lines +29 to +36
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
juju.wait(
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
)
juju.wait(
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
)

status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I misunderstood previous suggestions from Fouad, they may apply here too?

Comment on lines +59 to +64
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),

Comment on lines +60 to +65
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, any_charm_haproxy_route_requirer
)
),

Comment on lines 96 to +101
juju.deploy(
"postgresql",
app=POSTGRESQL_APPLICATION,
channel="16/edge",
base="ubuntu@24.04",
force=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity
why do we need force?
also what is the reason for deploy to have a force option in the first place?

Comment on lines +42 to +48
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, HAPROXY_ROUTE_REQUIRER_NAME
)
),
timeout=10 * 60,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
lambda status: (
status.apps[configured_application_with_tls].is_blocked
and jubilant.all_agents_idle(
status, configured_application_with_tls, HAPROXY_ROUTE_REQUIRER_NAME
)
),
timeout=10 * 60,
lambda status: (
jubilant.all_blocked(status, configured_application_with_tls)
and jubilant.all_agents_idle(
status, configured_application_with_tls, HAPROXY_ROUTE_REQUIRER_NAME
)
),
timeout=10 * 60,

Comment thread .gitignore
artifacts.build.yaml

.worktrees
**/superpowers/** No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline

Comment thread concierge-ck8s-juju3.yaml
k8s:
enable: true
bootstrap: false
channel: 1.32-classic/stable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not be on 1.35-classic/stable?

@f-atwi f-atwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
mostly nits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Libraries: Out of sync no-release-note This PR does not require a change artifact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants