Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
230 commits
Select commit Hold shift + click to select a range
7b7a688
Implement request API
Thanhphan1147 Mar 17, 2026
4540250
update model validation before save and add unit tests
Thanhphan1147 Apr 2, 2026
2111aa1
use environment variables for secret key
Thanhphan1147 Apr 2, 2026
b597201
ISD-5226 update docs landing pages (#387)
Thanhphan1147 Apr 2, 2026
0f1bbfd
ruff format
Thanhphan1147 Apr 2, 2026
2c7bd78
add secret key for testing
Thanhphan1147 Apr 2, 2026
cbd051e
remove port attribute from test
Thanhphan1147 Apr 2, 2026
eac8f31
add requirements.txt for testing
Thanhphan1147 Apr 2, 2026
45f1886
reintroduce port field
Thanhphan1147 Apr 2, 2026
4b5b49a
Add change artifact
Thanhphan1147 Apr 2, 2026
b143b06
run lint with uv
Thanhphan1147 Apr 2, 2026
02bb32f
add unit testing
Thanhphan1147 Apr 2, 2026
99f56fd
remove custom test
Thanhphan1147 Apr 2, 2026
e825d3a
update migration
Thanhphan1147 Apr 2, 2026
cceb382
Wrap creation under `transaction.atomic`
Thanhphan1147 Apr 2, 2026
e841faa
Potential fix for pull request finding
Thanhphan1147 Apr 2, 2026
597aa03
remove unused code
Thanhphan1147 Apr 2, 2026
5c9441d
minor fixes to settings
Thanhphan1147 Apr 2, 2026
9e981b6
use django serializer
Thanhphan1147 Apr 2, 2026
f4508e7
update gitignore
Thanhphan1147 Apr 2, 2026
6a49660
Potential fix for pull request finding
Thanhphan1147 Apr 2, 2026
ef41d0f
update view to use django rest
Thanhphan1147 Apr 2, 2026
447e25f
remove python-version
Thanhphan1147 Apr 2, 2026
2601119
update gitignore
Thanhphan1147 Apr 2, 2026
b7f3827
add missing license headers
Thanhphan1147 Apr 2, 2026
3d47843
Add rules engine
Thanhphan1147 Apr 2, 2026
4a066ea
update migration
Thanhphan1147 Apr 2, 2026
01e6da2
update view
Thanhphan1147 Apr 2, 2026
fc6ecc6
fix lint
Thanhphan1147 Apr 2, 2026
570def0
remove extra tests
Thanhphan1147 Apr 2, 2026
b5efa18
add validation and update tests
Thanhphan1147 Apr 2, 2026
96350a1
update view
Thanhphan1147 Apr 2, 2026
30eb7fb
remove to_dict
Thanhphan1147 Apr 2, 2026
c5fdbee
use serializer for get
Thanhphan1147 Apr 2, 2026
8d34c55
use serializer
Thanhphan1147 Apr 2, 2026
de68881
remove unused tests
Thanhphan1147 Apr 2, 2026
d568463
use filter for delete query
Thanhphan1147 Apr 2, 2026
cdfde2c
update tests and move validation to serializer class
Thanhphan1147 Apr 2, 2026
d9ad271
Apply suggestion from @github-actions[bot]
Thanhphan1147 Apr 2, 2026
bff1c28
remove license header from generated files
Thanhphan1147 Apr 2, 2026
1fad230
Update haproxy-route-policy/policy/migrations/0001_initial.py
Thanhphan1147 Apr 2, 2026
b5c511a
Revert "Update haproxy-route-policy/policy/migrations/0001_initial.py"
Thanhphan1147 Apr 2, 2026
9bd2821
ignore migration files for license header
Thanhphan1147 Apr 2, 2026
79510f9
add change artifact
Thanhphan1147 Apr 2, 2026
64b4792
add envlist to tox commands
Thanhphan1147 Apr 2, 2026
2c56b26
update envlist
Thanhphan1147 Apr 2, 2026
65d04cb
convert pk to uuid for requests
Thanhphan1147 Apr 2, 2026
4bdca17
Add guard against mal-formed uuid and parameter. Add logging configs,…
Thanhphan1147 Apr 2, 2026
97b8658
add validators for port and paths
Thanhphan1147 Apr 2, 2026
a187dd4
add tests for validators
Thanhphan1147 Apr 2, 2026
f48aed5
add note for migration
Thanhphan1147 Apr 2, 2026
3d5387a
remove unused imports
Thanhphan1147 Apr 2, 2026
0ac06b0
add static tests
Thanhphan1147 Apr 2, 2026
1020b70
guard rules API against pk
Thanhphan1147 Apr 2, 2026
9aee5c1
update view, middle wares and tests
Thanhphan1147 Apr 2, 2026
1328a47
chore(deps): update dependency haproxy-spoe-auth to v75 (#406)
Thanhphan1147 Apr 2, 2026
58149f4
refactor tests by parametrizing
Thanhphan1147 Apr 2, 2026
9207755
chore(deps): update dependency haproxy-spoe-auth to v77 (#408)
Thanhphan1147 Apr 2, 2026
8ea78d9
group tests by parameterizing
Thanhphan1147 Apr 2, 2026
8268b3d
refactor Rule model to rename attribute from "value" to "parameters"
Thanhphan1147 Apr 2, 2026
99b30ff
update test name
Thanhphan1147 Apr 2, 2026
f424e46
update naming
Thanhphan1147 Apr 2, 2026
b83802d
Add coverage-report as part of unit test suite
Thanhphan1147 Apr 2, 2026
0f4ab50
update env list
Thanhphan1147 Apr 2, 2026
fb49d58
implement rule evaluation
Thanhphan1147 Apr 2, 2026
cd413be
add change artifact
Thanhphan1147 Apr 2, 2026
fa372c1
update imports
Thanhphan1147 Apr 2, 2026
f21a128
update naming
Thanhphan1147 Apr 2, 2026
f4ad042
update rules matching logic
Thanhphan1147 Apr 2, 2026
334ca69
update tests
Thanhphan1147 Apr 2, 2026
3709511
save request using serializer with the correct instace
Thanhphan1147 Apr 2, 2026
c6d7870
Haproxy route policy rules api (#400)
Thanhphan1147 Apr 2, 2026
7a2ddf0
group tests
Thanhphan1147 Apr 2, 2026
d77225f
update formatting
Thanhphan1147 Apr 2, 2026
9075f59
Add authentication configuration for django-restframework and adapt t…
Thanhphan1147 Apr 2, 2026
9770b2d
add change artifact
Thanhphan1147 Apr 2, 2026
bb50277
Add token urls
Thanhphan1147 Apr 2, 2026
f94c20d
switch DB engine to postgres
Thanhphan1147 Apr 2, 2026
04557c5
add change artifact
Thanhphan1147 Apr 2, 2026
6e8989b
Add snap files
Thanhphan1147 Apr 2, 2026
d48faf0
fix build issue
Thanhphan1147 Apr 2, 2026
786efba
update module-name
Thanhphan1147 Apr 2, 2026
d24fab2
set secret key as default, drop fetching from file
Thanhphan1147 Apr 2, 2026
f2c6f0a
update readme, fix secret key generation
Thanhphan1147 Apr 2, 2026
e4c591a
add change artifact
Thanhphan1147 Apr 2, 2026
9da8970
add build snap workflow
Thanhphan1147 Apr 2, 2026
eed68e3
use upload-artifact v4
Thanhphan1147 Apr 2, 2026
d04a0f5
update path
Thanhphan1147 Apr 2, 2026
acb1801
add checkout step
Thanhphan1147 Apr 2, 2026
8541e19
change working dir for build action
Thanhphan1147 Apr 2, 2026
ffa0514
sparse checkout the policy directory
Thanhphan1147 Apr 2, 2026
2c4c6a1
debug
Thanhphan1147 Apr 2, 2026
0485276
debug path
Thanhphan1147 Apr 2, 2026
e583aec
remove debug
Thanhphan1147 Apr 2, 2026
c142c20
update scripts to guard against empty DB config values and update all…
Thanhphan1147 Apr 2, 2026
4621008
update docs link
Thanhphan1147 Apr 2, 2026
d1476c5
fix(deps): update all non-major dependencies (#381)
Thanhphan1147 Apr 2, 2026
3258f9d
fix(deps): update dependency cryptography to v46.0.6 [security] (#418)
Thanhphan1147 Apr 2, 2026
4155145
Haproxy route policy rules matching (#401)
Thanhphan1147 Apr 2, 2026
7de256c
chore: update Copilot collections to v0.8.0 (#419)
Thanhphan1147 Apr 2, 2026
eb052a9
use python3 to run manage script instead of uv
Thanhphan1147 Apr 2, 2026
b976869
bump snap version
Thanhphan1147 Apr 2, 2026
7a4b735
add spread test workflow (#420)
Thanhphan1147 Apr 2, 2026
3e84b1f
fix: add proper type to hosts in haproxy libraries (#383)
Thanhphan1147 Apr 2, 2026
16071b8
update docs for postgresql container
Thanhphan1147 Apr 2, 2026
36beabf
Update haproxy-route-policy/snap/hooks/configure
Thanhphan1147 Apr 2, 2026
c1f4ca3
address comments
Thanhphan1147 Apr 2, 2026
3fb2955
add tests for snap
Thanhphan1147 Apr 2, 2026
3f99f43
shell script lint fix
Thanhphan1147 Apr 2, 2026
6525181
update manage script
Thanhphan1147 Apr 2, 2026
f9141dc
revert script change
Thanhphan1147 Apr 2, 2026
3c83433
Merge remote-tracking branch 'origin/main' into haproxy-route-policy-…
Thanhphan1147 Apr 2, 2026
bcfff53
fix shellcheck errors
Thanhphan1147 Apr 3, 2026
1619a24
Fix drifts from main
Thanhphan1147 Apr 3, 2026
46b3ed5
remove 10s sleep
Thanhphan1147 Apr 3, 2026
cdc10e8
wait for snap service to settle
Thanhphan1147 Apr 3, 2026
27b2339
set config to start the snap
Thanhphan1147 Apr 3, 2026
4e7fd5e
add MVP for haproxy-roite-policy-operator
Thanhphan1147 Mar 31, 2026
09a8ee2
simplify charm mbp
Thanhphan1147 Mar 31, 2026
3c94b13
properly handles postgresql charm state and fix lint errors
Thanhphan1147 Mar 31, 2026
fa8b704
fix unit and reformat tox
Thanhphan1147 Mar 31, 2026
c20eb24
update snap config dict
Thanhphan1147 Mar 31, 2026
62823a9
update tox config and add integration test
Thanhphan1147 Mar 31, 2026
ca6449c
add change artifact
Thanhphan1147 Mar 31, 2026
d4d8ec3
run integration tests for haproxy-route-policy-operator
Thanhphan1147 Mar 31, 2026
7a2a4fd
add secret handling, update tests
Thanhphan1147 Mar 31, 2026
e1312a7
ignore bandit rules
Thanhphan1147 Mar 31, 2026
9b51388
add action, fix issue with command run
Thanhphan1147 Apr 1, 2026
c7b9a88
add change artifact
Thanhphan1147 Apr 1, 2026
8f27588
add upsertsuperuser command
Thanhphan1147 Apr 1, 2026
5de029c
block until peer relation and set a value after creating a secret to …
Thanhphan1147 Apr 1, 2026
49174ce
add peer relation definition and only run migration/update user if le…
Thanhphan1147 Apr 1, 2026
f81615e
move juju secret handling to charm state, add charm state for policy …
Thanhphan1147 Apr 2, 2026
75135c3
cast to string before dumping to json
Thanhphan1147 Apr 2, 2026
1d5f17c
add change artifact
Thanhphan1147 Apr 2, 2026
9524512
bootstrap lib
Thanhphan1147 Apr 3, 2026
39a8737
Merge remote-tracking branch 'origin/main' into haproxy-route-policy-…
Thanhphan1147 Apr 3, 2026
7e727e2
Merge branch 'main' into haproxy-route-policy-operator-create-admin-user
Thanhphan1147 Apr 7, 2026
bc72418
update fetch logic for admin credentials, update tests, update juju v…
Thanhphan1147 Apr 7, 2026
91ba2e3
move juju secret handling to charm state, add charm state for policy …
Thanhphan1147 Apr 2, 2026
e9d071d
cast to string before dumping to json
Thanhphan1147 Apr 2, 2026
3a15a1f
add change artifact
Thanhphan1147 Apr 2, 2026
6d46938
update handling of credentials
Thanhphan1147 Apr 7, 2026
e3933c5
bootstrap lib
Thanhphan1147 Apr 3, 2026
e80bcb7
bootstrap haproxy-route-policy lib
Thanhphan1147 Apr 3, 2026
60f3c1d
update lib, fix test issues
Thanhphan1147 Apr 7, 2026
6f6c652
add autoapprove logic and add integration tests with any-charm
Thanhphan1147 Apr 7, 2026
e1ba74f
Merge branch 'add_haproxy_route_policy_interface' of github.com:canon…
Thanhphan1147 Apr 7, 2026
054e536
update uv.lock
Thanhphan1147 Apr 7, 2026
60decd9
wait for complete relation data
Thanhphan1147 Apr 7, 2026
4742ce0
Merge branch 'add_haproxy_route_policy_interface' of github.com:canon…
Thanhphan1147 Apr 7, 2026
95993a8
update charm and requirer
Thanhphan1147 Apr 7, 2026
2436477
Merge branch 'add_haproxy_route_policy_interface' of github.com:canon…
Thanhphan1147 Apr 7, 2026
f044f1e
update integration tests
Thanhphan1147 Apr 7, 2026
ad83b03
remove unused lib and update tests and lib
Thanhphan1147 Apr 7, 2026
1764981
remove haproxy-route from charm-libs
Thanhphan1147 Apr 7, 2026
f2ef663
run integration test for haproxy-route-policy
Thanhphan1147 Apr 7, 2026
118ad05
ruff fmt
Thanhphan1147 Apr 7, 2026
f38ca87
add change artifact
Thanhphan1147 Apr 7, 2026
6589415
update change artifacts
Thanhphan1147 Apr 7, 2026
e59ef3c
fix failing tests
Thanhphan1147 Apr 10, 2026
62fceac
Merge branch 'main' into haproxy-route-policy-operator-create-admin-user
Thanhphan1147 Apr 10, 2026
f6609d8
Merge remote-tracking branch 'origin/haproxy-route-policy-operator-cr…
Thanhphan1147 Apr 10, 2026
abe5c3d
explicitly hint types, update uv lock, update tests
Thanhphan1147 Apr 10, 2026
b066edf
Merge remote-tracking branch 'origin/haproxy-route-policy-operator-ad…
Thanhphan1147 Apr 10, 2026
ee1b6d1
remove docstring
Thanhphan1147 Apr 10, 2026
1b83c06
update lib patch version
Thanhphan1147 Apr 10, 2026
9dce468
update business logic
Thanhphan1147 Apr 13, 2026
d950a5e
update logic to send haproxy route policy data to provider
Thanhphan1147 Apr 13, 2026
6b8589e
update password length checks
Thanhphan1147 Apr 13, 2026
8843d6b
Merge remote-tracking branch 'origin/haproxy-route-policy-operator-cr…
Thanhphan1147 Apr 13, 2026
7b0794e
Merge remote-tracking branch 'origin/haproxy-route-policy-operator-ad…
Thanhphan1147 Apr 13, 2026
a2e4462
don't use self-hosted runner for unit tests
Thanhphan1147 Apr 13, 2026
db91d9c
update unit test wf
Thanhphan1147 Apr 13, 2026
0060932
update runs-on tag
Thanhphan1147 Apr 13, 2026
e516c11
rename
Thanhphan1147 Apr 13, 2026
230830f
update unit tests
Thanhphan1147 Apr 13, 2026
f6f196d
Merge branch 'main' into haproxy-route-policy-operator-add-allowed-ho…
Thanhphan1147 Apr 13, 2026
c36f063
query the API to refresh backend requests
Thanhphan1147 Apr 13, 2026
ad2d2e8
remove merge errors
Thanhphan1147 Apr 13, 2026
fc78300
fix rendering bug for gprc backends
Thanhphan1147 Apr 13, 2026
0db3152
add test for grpc backend rendering
Thanhphan1147 Apr 13, 2026
45a17c7
Merge remote-tracking branch 'origin/haproxy-route-policy-operator-ad…
Thanhphan1147 Apr 13, 2026
1a02cd6
Merge remote-tracking branch 'origin/add_haproxy_route_policy_interfa…
Thanhphan1147 Apr 13, 2026
4be66e9
thin out the client
Thanhphan1147 Apr 13, 2026
dae3b3b
add relation interface, add global test
Thanhphan1147 Apr 13, 2026
e76ddb7
add relation interface
Thanhphan1147 Apr 13, 2026
19e1e91
Merge remote-tracking branch 'origin/update_haproxy_business_logic_in…
Thanhphan1147 Apr 13, 2026
47e74d8
Merge branch 'main' into add_haproxy_route_policy_interface
Thanhphan1147 Apr 13, 2026
9c9eebe
Merge remote-tracking branch 'origin/add_haproxy_route_policy_interfa…
Thanhphan1147 Apr 13, 2026
951e725
move exception handling to a separate state module, refactor charm code
Thanhphan1147 Apr 14, 2026
5d83e44
small fixes
Thanhphan1147 Apr 15, 2026
3eb170c
Merge branch 'main' into update_haproxy_business_logic_in_charm_code
Thanhphan1147 Apr 15, 2026
a1daae5
Merge branch 'main' into update_haproxy_business_logic_in_charm_code
Thanhphan1147 Apr 15, 2026
66bb54d
fix unit tests
Thanhphan1147 Apr 15, 2026
c84d970
Merge remote-tracking branch 'origin/update_haproxy_business_logic_in…
Thanhphan1147 Apr 15, 2026
2b9245f
add static assets
Thanhphan1147 Apr 15, 2026
9c0d188
update db schema, add static files to gunicorn, update business logic
Thanhphan1147 Apr 15, 2026
9fdbd00
update custom view in admin panel, fix small issue with model validation
Thanhphan1147 Apr 15, 2026
d187466
Update haproxy-route-policy-operator/charmcraft.yaml
Thanhphan1147 Apr 15, 2026
894df6d
fix issues in PR
Thanhphan1147 Apr 16, 2026
0417bac
minor fixes
Thanhphan1147 Apr 16, 2026
54b0d92
update method and add change artifact
Thanhphan1147 Apr 16, 2026
287dd1f
Merge remote-tracking branch 'origin/update_haproxy_business_logic_in…
Thanhphan1147 Apr 16, 2026
a69f90a
Merge branch 'haproxy_route_policy_query_api_and_update_approved_rule…
Thanhphan1147 Apr 16, 2026
e134d06
expose policy provider via a backend, update tests
Thanhphan1147 Apr 16, 2026
924578c
add trusted origins to snap config
Thanhphan1147 Apr 20, 2026
bd3d757
Merge branch 'main' into haproxy_route_policy_query_api_and_update_ap…
Thanhphan1147 Apr 20, 2026
a799b1d
set secure proxy header for haproxy
Thanhphan1147 Apr 20, 2026
d555a13
update template, conditionally render haproxy-route-policy backend wh…
Thanhphan1147 Apr 20, 2026
dbb59a7
add change artifact
Thanhphan1147 Apr 20, 2026
a8bcdce
Merge remote-tracking branch 'origin/haproxy_route_policy_query_api_a…
Thanhphan1147 Apr 20, 2026
de1b24a
add change artifact
Thanhphan1147 Apr 20, 2026
10d3dbc
Merge remote-tracking branch 'origin/add_static_assets_to_snap' into …
Thanhphan1147 Apr 20, 2026
1711ece
Add comments for django DEBUG mode
Thanhphan1147 Apr 20, 2026
fa1a13c
Add change artifact
Thanhphan1147 Apr 20, 2026
c453a8d
add logic to set allowed-hosts if haproxy has sent a proxied-endpoint
Thanhphan1147 Apr 20, 2026
61ac072
send policy hostname via relation data
Thanhphan1147 Apr 20, 2026
3887a3b
update scheme in hostname
Thanhphan1147 Apr 20, 2026
4244a45
update lib for serialization and update logic
Thanhphan1147 Apr 21, 2026
41fa10c
Merge branch 'main' into expose_haproxy_route_policy_service
Thanhphan1147 Apr 21, 2026
ebf5bdd
fix merge conflicts
Thanhphan1147 Apr 21, 2026
6778bae
resolve merge conflicts
Thanhphan1147 Apr 21, 2026
cd9ba2f
resolve merge conflicts, update snap, remove unused conf
Thanhphan1147 Apr 21, 2026
8672856
resolve conflicts
Thanhphan1147 Apr 21, 2026
0dda1b7
Merge remote-tracking branch 'origin/expose_haproxy_route_policy_serv…
Thanhphan1147 Apr 21, 2026
647f20f
add change artifact
Thanhphan1147 Apr 21, 2026
a39e544
Merge remote-tracking branch 'origin/main' into automatically_configu…
Thanhphan1147 Apr 21, 2026
3833188
update lib schema to not send a HttpUrl but send the hostname directly
Thanhphan1147 Apr 22, 2026
d97a984
Merge branch 'main' into automatically_configure_allowed_hosts_for_ha…
Thanhphan1147 Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/release-notes/artifacts/pr0475.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version_schema: 2

changes:
- title: Publish proxied endpoint URL to policy provider and auto-add host to
allowed-hosts
author: tphan025
type: minor
description: >
Add a new proxied_endpoint field on the requirer app data. The
haproxy-operator now publishes the generated hostname. On the
policy-operator side, the charm extracts the host from the proxied
endpoint and appends it to the Django allowed-hosts list. Refactored
HaproxyRoutePolicyInformation to rename allowed_hosts to
extra_allowed_hosts. Updated unit tests accordingly.
urls:
pr:
- https://github.com/canonical/haproxy-operator/pull/475
related_doc:
related_issue:
visibility: public
highlight: false
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 4
LIBPATCH = 8


def valid_domain_with_wildcard(value: str) -> str:
Expand All @@ -61,6 +61,17 @@ def valid_domain_with_wildcard(value: str) -> str:
return value


def valid_domain(value: str) -> str:
"""Validate if value is a valid domain without wildcards.

Raises:
ValueError: When value is not a valid domain.
"""
if not bool(domain(value)):
raise ValueError(f"Invalid domain: {value}")
return value


logger = logging.getLogger(__name__)
HAPROXY_ROUTE_POLICY_RELATION_NAME = "haproxy-route-policy"

Expand Down Expand Up @@ -101,6 +112,9 @@ class HaproxyRoutePolicyRequirerAppData:
backend_requests: list[HaproxyRoutePolicyBackendRequest] = Field(
description="List of backends to be evaluated by the policy service."
)
proxied_endpoint: Annotated[str, BeforeValidator(valid_domain)] | None = Field(
description=("URL for the proxied endpoint that's exposing the Django web UI."),
)

@model_validator(mode="after")
def validate_unique_backend_names(self):
Expand Down Expand Up @@ -201,15 +215,20 @@ def relation(self) -> Relation | None:
return self.charm.model.get_relation(self._relation_name)

def provide_haproxy_route_policy_requests(
self, backend_requests: list[HaproxyRoutePolicyBackendRequest]
self,
backend_requests: list[HaproxyRoutePolicyBackendRequest],
proxied_endpoint: str | None,
) -> None:
"""Set and publish route policy requests."""
relation = self.relation
if not relation or not self.charm.unit.is_leader():
return

try:
app_data = HaproxyRoutePolicyRequirerAppData(backend_requests=backend_requests)
app_data = HaproxyRoutePolicyRequirerAppData(
backend_requests=backend_requests,
proxied_endpoint=proxied_endpoint,
)
relation.save(app_data, self.charm.app)
except (
ValidationError,
Expand Down
8 changes: 4 additions & 4 deletions haproxy-operator/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,10 @@ def _configure_haproxy_route(
)
if self.unit.is_leader() and self.haproxy_route_policy.relation is not None:
self.haproxy_route_policy.provide_haproxy_route_policy_requests(
haproxy_route_requirers_information.backend_requests_for_policy
haproxy_route_requirers_information.backend_requests_for_policy,
haproxy_route_requirers_information.policy_provider_backend.hostname
if haproxy_route_requirers_information.policy_provider_backend
else None,
)
# We ONLY allow the charm to run with no certificate requested if:
# 1. there's only haproxy-route-tcp relations
Expand Down Expand Up @@ -409,9 +412,6 @@ def _configure_haproxy_route(
),
)
if self.unit.is_leader():
self.haproxy_route_policy.provide_haproxy_route_policy_requests(
haproxy_route_requirers_information.backend_requests_for_policy
)
self._publish_haproxy_route_proxied_endpoints(haproxy_route_requirers_information)
self._publish_haproxy_route_tcp_proxied_endpoints(
haproxy_route_requirers_information, ha_information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 4
LIBPATCH = 8


def valid_domain_with_wildcard(value: str) -> str:
Expand All @@ -61,6 +61,17 @@ def valid_domain_with_wildcard(value: str) -> str:
return value


def valid_domain(value: str) -> str:
"""Validate if value is a valid domain without wildcards.

Raises:
ValueError: When value is not a valid domain.
"""
if not bool(domain(value)):
raise ValueError(f"Invalid domain: {value}")
return value


logger = logging.getLogger(__name__)
HAPROXY_ROUTE_POLICY_RELATION_NAME = "haproxy-route-policy"

Expand Down Expand Up @@ -101,6 +112,9 @@ class HaproxyRoutePolicyRequirerAppData:
backend_requests: list[HaproxyRoutePolicyBackendRequest] = Field(
description="List of backends to be evaluated by the policy service."
)
proxied_endpoint: Annotated[str, BeforeValidator(valid_domain)] | None = Field(
description=("URL for the proxied endpoint that's exposing the Django web UI."),
)

@model_validator(mode="after")
def validate_unique_backend_names(self):
Expand Down Expand Up @@ -201,15 +215,20 @@ def relation(self) -> Relation | None:
return self.charm.model.get_relation(self._relation_name)

def provide_haproxy_route_policy_requests(
self, backend_requests: list[HaproxyRoutePolicyBackendRequest]
self,
backend_requests: list[HaproxyRoutePolicyBackendRequest],
proxied_endpoint: str | None,
) -> None:
"""Set and publish route policy requests."""
relation = self.relation
if not relation or not self.charm.unit.is_leader():
return

try:
app_data = HaproxyRoutePolicyRequirerAppData(backend_requests=backend_requests)
app_data = HaproxyRoutePolicyRequirerAppData(
backend_requests=backend_requests,
proxied_endpoint=proxied_endpoint,
)
relation.save(app_data, self.charm.app)
except (
ValidationError,
Expand Down
28 changes: 20 additions & 8 deletions haproxy-route-policy-operator/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

"""haproxy-route-policy-operator charm."""

import json
import logging
from typing import Any

Expand All @@ -22,6 +23,7 @@
configure_snap,
create_or_update_user,
install_snap,
is_service_active,
run_migrations,
start_gunicorn_service,
)
Expand Down Expand Up @@ -94,9 +96,24 @@ def _reconcile(self, _: ops.EventBase) -> None:
self.unit.status = ops.MaintenanceStatus("configuring haproxy-route-policy")
database_information = DatabaseInformation.from_requirer(self, self.database)
haproxy_route_policy_information = HaproxyRoutePolicyInformation.from_charm(self)

allowed_hosts = haproxy_route_policy_information.allowed_hosts_configuration
if relation := self.haproxy_route_policy.relation:
haproxy_route_policy_requirer_data = relation.load(
HaproxyRoutePolicyRequirerAppData, relation.app
)
if is_service_active():
# We can only send requests to the policy API if the service is active.
self._fetch_and_refresh_backend_requests(
haproxy_route_policy_information, haproxy_route_policy_requirer_data
)

if proxied_endpoint := haproxy_route_policy_requirer_data.proxied_endpoint:
allowed_hosts.append(proxied_endpoint)

configure_snap(
{
**haproxy_route_policy_information.allowed_hosts_snap_configuration,
**{"allowed-hosts": json.dumps(allowed_hosts)},
**database_information.haproxy_route_policy_snap_configuration,
}
)
Expand All @@ -116,9 +133,6 @@ def _reconcile(self, _: ops.EventBase) -> None:

self.unit.open_port("tcp", HAPROXY_ROUTE_POLICY_PORT)

if relation := self.haproxy_route_policy.relation:
self._fetch_and_refresh_backend_requests(haproxy_route_policy_information, relation)

self.unit.status = ops.ActiveStatus()

def _on_get_admin_credentials_action(self, event: ops.ActionEvent) -> None:
Expand All @@ -140,12 +154,10 @@ def _on_get_admin_credentials_action(self, event: ops.ActionEvent) -> None:
def _fetch_and_refresh_backend_requests(
self,
haproxy_route_policy_information: HaproxyRoutePolicyInformation,
haproxy_route_policy_relation: ops.Relation,
haproxy_route_policy_requirer_data: HaproxyRoutePolicyRequirerAppData,
) -> None:
"""Fetch backend requests from relation and refresh their status via the policy API."""
backend_requests = haproxy_route_policy_relation.load(
HaproxyRoutePolicyRequirerAppData, haproxy_route_policy_relation.app
).backend_requests
backend_requests = haproxy_route_policy_requirer_data.backend_requests

client = HaproxyRoutePolicyClient(
username=haproxy_route_policy_information.admin_username,
Expand Down
6 changes: 6 additions & 0 deletions haproxy-route-policy-operator/src/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ def start_gunicorn_service() -> None:
package.start()


def is_service_active() -> bool:
"""Check if the snap gunicorn app is active."""
package = snap.SnapCache()[SNAP_NAME]
return package.services["haproxy-route-policy"].get("active", False)


def create_or_update_user(username: str, password: str) -> None:
"""Create or update the HTTP proxy policy superuser.

Expand Down
21 changes: 10 additions & 11 deletions haproxy-route-policy-operator/src/state/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

"""Charm state for HAProxy route policy information."""

import json
import secrets
from typing import Annotated, cast

Expand Down Expand Up @@ -66,19 +65,19 @@ class HaproxyRoutePolicyInformation:
secret_key: Django secret key.
"""

allowed_hosts: list[FQDN | IPvAnyAddress] = Field()
extra_allowed_hosts: list[FQDN | IPvAnyAddress] = Field()
admin_username: str = Field()
admin_password: str = Field()
secret_key: str = Field()

@property
def allowed_hosts_snap_configuration(self) -> dict[str, str]:
"""Return snap configuration keys and values."""
return {
"allowed-hosts": json.dumps(
DEFAULT_ALLOWED_HOSTS + [str(host) for host in self.allowed_hosts]
),
}
def allowed_hosts_configuration(self) -> list[str]:
"""Get the allowed hosts snap configuration.

Returns:
list: The allowed hosts to set in snap configuration.
"""
return DEFAULT_ALLOWED_HOSTS + [str(host) for host in self.extra_allowed_hosts]

@classmethod
def from_charm(cls, charm: ops.CharmBase) -> "HaproxyRoutePolicyInformation":
Expand All @@ -94,7 +93,7 @@ def from_charm(cls, charm: ops.CharmBase) -> "HaproxyRoutePolicyInformation":
if not peer_relation:
raise PeerRelationMissingError("Peer relation is missing.")

allowed_hosts = (
extra_allowed_hosts = (
[
cast(IPvAnyAddress | FQDN, address)
for address in cast(str, charm.config.get("extra-allowed-hosts")).split(",")
Expand All @@ -109,7 +108,7 @@ def from_charm(cls, charm: ops.CharmBase) -> "HaproxyRoutePolicyInformation":
)
secret_key = _get_django_secret_key(charm, peer_relation)["secret-key"]
return cls(
allowed_hosts=allowed_hosts,
extra_allowed_hosts=extra_allowed_hosts,
admin_username=credentials["username"],
admin_password=credentials["password"],
secret_key=secret_key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
def _build_state(allowed_hosts: list[str]) -> HaproxyRoutePolicyInformation:
"""Build a valid state instance with overridable allowed hosts."""
return HaproxyRoutePolicyInformation(
allowed_hosts=cast(list[Any], allowed_hosts),
extra_allowed_hosts=cast(list[Any], allowed_hosts),
admin_username="admin",
# Ignore bandit warning as this is for testing.
admin_password="secret", # nosec
Expand All @@ -25,15 +25,15 @@ def _build_state(allowed_hosts: list[str]) -> HaproxyRoutePolicyInformation:
@pytest.mark.parametrize(
"allowed_hosts, expected_allowed_hosts",
[
pytest.param([], [], id="empty-list"),
pytest.param(["example.com"], ["example.com"], id="single-fqdn"),
pytest.param([], ["localhost"], id="empty-list"),
pytest.param(["example.com"], ["localhost", "example.com"], id="single-fqdn"),
pytest.param(
["example.com", "api.example.com"],
["example.com", "api.example.com"],
["localhost", "example.com", "api.example.com"],
id="multiple-fqdn",
),
pytest.param(["10.0.0.10"], ["10.0.0.10"], id="ipv4-address"),
pytest.param(["2001:db8::1"], ["2001:db8::1"], id="ipv6-address"),
pytest.param(["10.0.0.10"], ["localhost", "10.0.0.10"], id="ipv4-address"),
pytest.param(["2001:db8::1"], ["localhost", "2001:db8::1"], id="ipv6-address"),
],
)
def test_haproxy_route_policy_information_init_valid_allowed_hosts(
Expand All @@ -46,7 +46,7 @@ def test_haproxy_route_policy_information_init_valid_allowed_hosts(
"""
state = _build_state(allowed_hosts)

assert [str(host) for host in state.allowed_hosts] == expected_allowed_hosts
assert [str(host) for host in state.allowed_hosts_configuration] == expected_allowed_hosts


@pytest.mark.parametrize(
Expand Down Expand Up @@ -94,25 +94,3 @@ def test_haproxy_route_policy_information_init_rejects_none_string_fields(

with pytest.raises(ValidationError):
HaproxyRoutePolicyInformation(**payload)


@pytest.mark.parametrize(
"allowed_hosts, expected",
[
pytest.param([], {"allowed-hosts": '["localhost"]'}, id="empty"),
pytest.param(
["example.com", "api.example.com"],
{"allowed-hosts": '["localhost", "example.com", "api.example.com"]'},
id="multiple-fqdn",
),
],
)
def test_allowed_hosts_snap_configuration(allowed_hosts: list[str], expected: dict[str, str]):
"""
arrange: initialize state with valid allowed hosts.
act: read snap configuration property.
assert: allowed-hosts is serialized to expected JSON string.
"""
state = _build_state(allowed_hosts)

assert state.allowed_hosts_snap_configuration == expected
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ def test_requirer_app_data_model_accepts_valid_payload():
assert: payload is validated and fields are preserved.
"""
request = HaproxyRoutePolicyBackendRequest(**VALID_BACKEND_REQUEST)
app_data = HaproxyRoutePolicyRequirerAppData(backend_requests=[request])
app_data = HaproxyRoutePolicyRequirerAppData(
backend_requests=[request], proxied_endpoint="example.com"
)

assert len(app_data.backend_requests) == 1
assert app_data.backend_requests[0].backend_name == "backend-a"
Expand Down Expand Up @@ -142,4 +144,6 @@ def test_requirer_app_data_rejects_duplicate_backend_names():
]

with pytest.raises(ValidationError):
HaproxyRoutePolicyRequirerAppData(backend_requests=duplicated_requests)
HaproxyRoutePolicyRequirerAppData(
backend_requests=duplicated_requests, proxied_endpoint=None
)
Loading
Loading