Skip to content

Commit 3423084

Browse files
committed
Resolve conflict
2 parents 34fba05 + e02c366 commit 3423084

File tree

9 files changed

+356
-278
lines changed

9 files changed

+356
-278
lines changed

CHANGELOG.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,76 @@
11
# CHANGELOG
22

33

4+
## v0.26.4 (2025-10-14)
5+
6+
### Chores
7+
8+
- Bump cryptography from 45.0.7 to 46.0.1
9+
([#791](https://github.com/Twingate/kubernetes-operator/pull/791),
10+
[`4ba9d0a`](https://github.com/Twingate/kubernetes-operator/commit/4ba9d0ac40eaa283846d535fa58ac740157d6999))
11+
12+
- Bump cryptography from 46.0.1 to 46.0.2
13+
([#801](https://github.com/Twingate/kubernetes-operator/pull/801),
14+
[`fc8fdd8`](https://github.com/Twingate/kubernetes-operator/commit/fc8fdd8398f1eb674c4db59e4cbaaf44745407ee))
15+
16+
- Bump hadolint/hadolint-action from 3.2.0 to 3.3.0
17+
([#796](https://github.com/Twingate/kubernetes-operator/pull/796),
18+
[`cbd7460`](https://github.com/Twingate/kubernetes-operator/commit/cbd74606486bffd377376ba5513de90801aa9408))
19+
20+
- Bump kubernetes from 33.1.0 to 34.1.0
21+
([#800](https://github.com/Twingate/kubernetes-operator/pull/800),
22+
[`47b7329`](https://github.com/Twingate/kubernetes-operator/commit/47b73299b271b20b2e307542b9c7069d6e38a229))
23+
24+
- Bump kubernetes-access-gateway from 0.10.0 to 0.10.1 in /deploy/twingate-operator
25+
([#811](https://github.com/Twingate/kubernetes-operator/pull/811),
26+
[`01728e5`](https://github.com/Twingate/kubernetes-operator/commit/01728e561ff2d60f9825d78642046e2d5e47a45b))
27+
28+
- Bump mypy from 1.18.1 to 1.18.2 ([#794](https://github.com/Twingate/kubernetes-operator/pull/794),
29+
[`4846803`](https://github.com/Twingate/kubernetes-operator/commit/4846803c76c3437bc1864f04927be320fffbe72d))
30+
31+
- Bump pydantic from 2.11.10 to 2.12.0
32+
([#805](https://github.com/Twingate/kubernetes-operator/pull/805),
33+
[`a5699d6`](https://github.com/Twingate/kubernetes-operator/commit/a5699d65611c6dda427e72129c85685cf8be2ac0))
34+
35+
- Bump pydantic from 2.11.9 to 2.11.10
36+
([#803](https://github.com/Twingate/kubernetes-operator/pull/803),
37+
[`c714bdc`](https://github.com/Twingate/kubernetes-operator/commit/c714bdce7379398af23b2af88fade7c551952bea))
38+
39+
- Bump pydantic from 2.12.0 to 2.12.1
40+
([#810](https://github.com/Twingate/kubernetes-operator/pull/810),
41+
[`8c1b0c7`](https://github.com/Twingate/kubernetes-operator/commit/8c1b0c7d44438df14393754a7fa4231e94c77bca))
42+
43+
- Bump pydantic from 2.12.1 to 2.12.2
44+
([#812](https://github.com/Twingate/kubernetes-operator/pull/812),
45+
[`318d603`](https://github.com/Twingate/kubernetes-operator/commit/318d603f650c3cb6c33e1aa1a5af9f4716ce38ca))
46+
47+
- Bump pydantic-settings from 2.10.1 to 2.11.0
48+
([#797](https://github.com/Twingate/kubernetes-operator/pull/797),
49+
[`71a17c0`](https://github.com/Twingate/kubernetes-operator/commit/71a17c0eef97319a337e9ee759535de48680f363))
50+
51+
- Bump pyupgrade from 3.20.0 to 3.21.0
52+
([#809](https://github.com/Twingate/kubernetes-operator/pull/809),
53+
[`be5c327`](https://github.com/Twingate/kubernetes-operator/commit/be5c327b150499217c06ffa998461363ff2e3013))
54+
55+
- Bump ruff from 0.13.0 to 0.13.1 ([#793](https://github.com/Twingate/kubernetes-operator/pull/793),
56+
[`3877370`](https://github.com/Twingate/kubernetes-operator/commit/3877370f6c5e2c6463e47cf847d9c0c3c13ac8a3))
57+
58+
- Bump ruff from 0.13.1 to 0.13.2 ([#798](https://github.com/Twingate/kubernetes-operator/pull/798),
59+
[`65957fa`](https://github.com/Twingate/kubernetes-operator/commit/65957fa343390024e56b4dc1431c3d218ee1d48f))
60+
61+
- Bump ruff from 0.13.2 to 0.13.3 ([#802](https://github.com/Twingate/kubernetes-operator/pull/802),
62+
[`88331fe`](https://github.com/Twingate/kubernetes-operator/commit/88331fe3685a772c9d88aeb5263a83ce4e66dbf8))
63+
64+
- Bump ruff from 0.13.3 to 0.14.0 ([#806](https://github.com/Twingate/kubernetes-operator/pull/806),
65+
[`8542681`](https://github.com/Twingate/kubernetes-operator/commit/854268163d9955df5844f9bb0753fa148c58b3b1))
66+
67+
- Bump syrupy from 4.9.1 to 5.0.0 ([#799](https://github.com/Twingate/kubernetes-operator/pull/799),
68+
[`c639ac1`](https://github.com/Twingate/kubernetes-operator/commit/c639ac1328b024ec3b8697b7eb83502be7563e7d))
69+
70+
- Upgrade Poetry to 2.2.1 ([#804](https://github.com/Twingate/kubernetes-operator/pull/804),
71+
[`96098a7`](https://github.com/Twingate/kubernetes-operator/commit/96098a776f2159d4a8150456c5fe3061d56f4d5f))
72+
73+
474
## v0.26.3 (2025-09-17)
575

676
### Bug Fixes

app/crds.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
from semantic_version import NpmSpec
2424

2525
from app.settings import get_settings
26-
from app.utils_k8s import get_ca_cert, k8s_get_secret
26+
from app.utils import validate_pem_x509_certificate
27+
from app.utils_k8s import k8s_read_namespaced_secret
2728
from app.version_policy_providers import get_provider
2829

2930
K8sObject = MutableMapping[Any, Any]
@@ -131,14 +132,34 @@ class ResourceProxy(BaseModel):
131132

132133
def get_certificate_authority_cert(self) -> str | None:
133134
if secret_ref := self.certificate_authority_cert_secret_ref:
134-
secret = k8s_get_secret(secret_ref.namespace, secret_ref.name)
135-
if not secret:
136-
return None
135+
if secret := k8s_read_namespaced_secret(
136+
secret_ref.namespace, secret_ref.name
137+
):
138+
return self.read_certificate_authority_cert_from_secret(secret)
137139

138-
return base64.b64decode(get_ca_cert(secret)).decode()
140+
return None
139141

140142
return self.certificate_authority_cert
141143

144+
@staticmethod
145+
def read_certificate_authority_cert_from_secret(
146+
secret: kubernetes.client.V1Secret,
147+
) -> str:
148+
secret_name = secret.metadata.name
149+
if not (ca_cert := secret.data.get("ca.crt")):
150+
raise kopf.PermanentError(
151+
f"Kubernetes Secret object: {secret_name} is missing ca.crt."
152+
)
153+
154+
try:
155+
ca_cert = base64.b64decode(ca_cert).decode()
156+
validate_pem_x509_certificate(ca_cert)
157+
return ca_cert
158+
except ValueError as ex:
159+
raise kopf.PermanentError(
160+
f"Kubernetes Secret object: {secret_name} ca.crt is invalid."
161+
) from ex
162+
142163

143164
class ResourceType(StrEnum):
144165
NETWORK = "Network"

app/tests/test_crds_resource.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
TwingateResourceCRD,
1313
_KubernetesObjectRef,
1414
)
15-
from app.utils_k8s import get_ca_cert
1615

1716

1817
@pytest.fixture
@@ -304,9 +303,12 @@ def test_resource_proxy_get_certificate_authority_cert_with_secret_ref(
304303
)
305304
k8s_core_client_mock.read_namespaced_secret.return_value = k8s_secret_mock
306305

307-
with patch("app.crds.get_ca_cert", wraps=get_ca_cert) as get_ca_cert_mock:
306+
with patch(
307+
"app.crds.ResourceProxy.read_certificate_authority_cert_from_secret",
308+
wraps=proxy.read_certificate_authority_cert_from_secret,
309+
) as read_ca_cert_mock:
308310
assert proxy.get_certificate_authority_cert() == VALID_CA_CERT
309-
get_ca_cert_mock.assert_called_once_with(k8s_secret_mock)
311+
read_ca_cert_mock.assert_called_once_with(k8s_secret_mock)
310312

311313

312314
def test_network_resource_spec_to_graphql_arguments(sample_network_resource_object):
@@ -410,3 +412,31 @@ def test_resource_proxy_certificate_authority_cert_should_trim_whitespace(
410412
)
411413

412414
assert resource_spec.proxy.certificate_authority_cert == VALID_CA_CERT
415+
416+
417+
class TestResourceProxyReadCACertFromSecret:
418+
def test_read_ca_cert_from_secret(self, k8s_secret_mock):
419+
assert (
420+
ResourceProxy.read_certificate_authority_cert_from_secret(k8s_secret_mock)
421+
== VALID_CA_CERT
422+
)
423+
424+
def test_read_ca_cert_from_secret_with_missing_ca_cert(self, k8s_secret_mock):
425+
k8s_secret_mock.data = {}
426+
427+
with pytest.raises(
428+
kopf.PermanentError,
429+
match=r"Kubernetes Secret object: gateway-tls is missing ca.crt.",
430+
):
431+
ResourceProxy.read_certificate_authority_cert_from_secret(k8s_secret_mock)
432+
433+
def test_read_ca_cert_from_secret_with_invalid_ca_cert(self, k8s_secret_mock):
434+
k8s_secret_mock.data["ca.crt"] = (
435+
"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tIE1JSUZmakNDQTJhZ0F3SUJBZ0lVQk50IC0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0="
436+
)
437+
438+
with pytest.raises(
439+
kopf.PermanentError,
440+
match=r"Kubernetes Secret object: gateway-tls ca.crt is invalid.",
441+
):
442+
ResourceProxy.read_certificate_authority_cert_from_secret(k8s_secret_mock)

app/tests/test_utils_k8s.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import kopf
21
import kubernetes
32
import pytest
43

5-
from app.api.tests.factories import BASE64_OF_VALID_CA_CERT
64
from app.utils_k8s import (
7-
get_ca_cert,
85
k8s_delete_pod,
9-
k8s_get_secret,
106
k8s_read_namespaced_deployment,
117
k8s_read_namespaced_pod,
8+
k8s_read_namespaced_secret,
129
)
1310

1411

@@ -62,41 +59,16 @@ def test_reraises_non_404_exceptions(self, k8s_apps_client_mock):
6259
k8s_read_namespaced_deployment("default", "test")
6360

6461

65-
class TestK8sGetTLSSecret:
62+
class TestReadNamespacedSecret:
6663
def test_handles_404_returns_none(self, k8s_core_client_mock):
6764
k8s_core_client_mock.read_namespaced_secret.side_effect = (
6865
kubernetes.client.exceptions.ApiException(status=404)
6966
)
70-
assert k8s_get_secret("default", "test") is None
67+
assert k8s_read_namespaced_secret("default", "test") is None
7168

7269
def test_reraises_non_404_exceptions(self, k8s_core_client_mock):
7370
k8s_core_client_mock.read_namespaced_secret.side_effect = (
7471
kubernetes.client.exceptions.ApiException(status=500)
7572
)
7673
with pytest.raises(kubernetes.client.exceptions.ApiException):
77-
k8s_get_secret("default", "test")
78-
79-
80-
class TestGetCACert:
81-
def test_get_ca_cert(self, k8s_secret_mock):
82-
assert get_ca_cert(k8s_secret_mock) == BASE64_OF_VALID_CA_CERT
83-
84-
def test_get_ca_cert_with_missing_ca_cert(self, k8s_secret_mock):
85-
k8s_secret_mock.data = {}
86-
87-
with pytest.raises(
88-
kopf.PermanentError,
89-
match=r"Kubernetes Secret object: gateway-tls is missing ca.crt.",
90-
):
91-
get_ca_cert(k8s_secret_mock)
92-
93-
def test_get_ca_cert_with_invalid_ca_cert(self, k8s_secret_mock):
94-
k8s_secret_mock.data["ca.crt"] = (
95-
"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tIE1JSUZmakNDQTJhZ0F3SUJBZ0lVQk50IC0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0="
96-
)
97-
98-
with pytest.raises(
99-
kopf.PermanentError,
100-
match=r"Kubernetes Secret object: gateway-tls ca.crt is invalid.",
101-
):
102-
get_ca_cert(k8s_secret_mock)
74+
k8s_read_namespaced_secret("default", "test")

app/utils_k8s.py

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import base64
2-
3-
import kopf
41
import kubernetes
52

6-
from app.utils import validate_pem_x509_certificate
7-
83

94
def k8s_read_namespaced_pod(
105
namespace: str, name: str, kapi: kubernetes.client.CoreV1Api | None = None
@@ -47,30 +42,15 @@ def k8s_read_namespaced_deployment(
4742
raise
4843

4944

50-
def k8s_get_secret(namespace: str, name: str) -> kubernetes.client.V1Secret | None:
45+
def k8s_read_namespaced_secret(
46+
namespace: str, name: str, kapi: kubernetes.client.CoreV1Api | None = None
47+
) -> kubernetes.client.V1Secret | None:
5148
try:
52-
return kubernetes.client.CoreV1Api().read_namespaced_secret(
53-
name=name, namespace=namespace
54-
)
49+
kapi = kapi or kubernetes.client.CoreV1Api()
50+
51+
return kapi.read_namespaced_secret(name=name, namespace=namespace)
5552
except kubernetes.client.exceptions.ApiException as ex:
5653
if ex.status == 404:
5754
return None
5855

5956
raise
60-
61-
62-
def get_ca_cert(secret: kubernetes.client.V1Secret) -> str:
63-
secret_name = secret.metadata.name
64-
if not (ca_cert := secret.data.get("ca.crt")):
65-
raise kopf.PermanentError(
66-
f"Kubernetes Secret object: {secret_name} is missing ca.crt."
67-
)
68-
69-
try:
70-
validate_pem_x509_certificate(base64.b64decode(ca_cert).decode())
71-
except ValueError as ex:
72-
raise kopf.PermanentError(
73-
f"Kubernetes Secret object: {secret_name} ca.crt is invalid."
74-
) from ex
75-
76-
return ca_cert
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
dependencies:
22
- name: kubernetes-access-gateway
33
repository: oci://ghcr.io/twingate/helmcharts
4-
version: 0.10.0
5-
digest: sha256:7819eee88fa4ff84b2310446b0f6631153895ac32ad448ec68f081d19df0d2bd
6-
generated: "2025-08-29T16:26:39.587394325Z"
4+
version: 0.10.1
5+
digest: sha256:d1262910ff91cc3c7ec34a5e516aa6a37439c8b840d5c62e96e621997372a9c4
6+
generated: "2025-10-14T16:25:49.614357787Z"

deploy/twingate-operator/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ sources:
99
- https://github.com/Twingate/kubernetes-operator
1010
dependencies:
1111
- name: kubernetes-access-gateway
12-
version: 0.10.0
12+
version: 0.10.1
1313
repository: "oci://ghcr.io/twingate/helmcharts"
1414
condition: kubernetes-access-gateway.enabled

0 commit comments

Comments
 (0)