66This class handles certificate request and renewal through
77the interaction with the TLS Certificates Operator.
88
9- This library needs that https://charmhub.io/tls-certificates-interface/libraries/tls_certificates
10- library is imported to work.
9+ This library needs that the following libraries are imported to work:
10+ - https://charmhub.io/certificate-transfer-interface/libraries/certificate_transfer
11+ - https://charmhub.io/tls-certificates-interface/libraries/tls_certificates
1112
1213It also needs the following methods in the charm class:
1314— get_hostname_by_unit: to retrieve the DNS hostname of the unit.
2425import socket
2526from typing import List , Optional
2627
28+ from charms .certificate_transfer_interface .v0 .certificate_transfer import (
29+ CertificateAvailableEvent as CertificateAddedEvent ,
30+ )
31+ from charms .certificate_transfer_interface .v0 .certificate_transfer import (
32+ CertificateRemovedEvent as CertificateRemovedEvent ,
33+ )
34+ from charms .certificate_transfer_interface .v0 .certificate_transfer import (
35+ CertificateTransferRequires ,
36+ )
2737from charms .tls_certificates_interface .v2 .tls_certificates import (
2838 CertificateAvailableEvent ,
2939 CertificateExpiringEvent ,
4555
4656# Increment this PATCH version before using `charmcraft publish-lib` or reset
4757# to 0 if you are raising the major API version.
48- LIBPATCH = 13
58+ LIBPATCH = 14
4959
5060logger = logging .getLogger (__name__ )
5161SCOPE = "unit"
52- TLS_RELATION = "certificates"
62+ TLS_CREATION_RELATION = "certificates"
63+ TLS_TRANSFER_RELATION = "receive-ca-cert"
5364
5465
5566class PostgreSQLTLS (Object ):
@@ -63,18 +74,29 @@ def __init__(
6374 self .charm = charm
6475 self .peer_relation = peer_relation
6576 self .additional_dns_names = additional_dns_names or []
66- self .certs = TLSCertificatesRequiresV2 (self .charm , TLS_RELATION )
77+ self .certs_creation = TLSCertificatesRequiresV2 (self .charm , TLS_CREATION_RELATION )
78+ self .certs_transfer = CertificateTransferRequires (self .charm , TLS_TRANSFER_RELATION )
6779 self .framework .observe (
6880 self .charm .on .set_tls_private_key_action , self ._on_set_tls_private_key
6981 )
7082 self .framework .observe (
71- self .charm .on [TLS_RELATION ].relation_joined , self ._on_tls_relation_joined
83+ self .charm .on [TLS_CREATION_RELATION ].relation_joined , self ._on_tls_relation_joined
84+ )
85+ self .framework .observe (
86+ self .charm .on [TLS_CREATION_RELATION ].relation_broken , self ._on_tls_relation_broken
87+ )
88+ self .framework .observe (
89+ self .certs_creation .on .certificate_available , self ._on_certificate_available
90+ )
91+ self .framework .observe (
92+ self .certs_creation .on .certificate_expiring , self ._on_certificate_expiring
7293 )
7394 self .framework .observe (
74- self .charm .on [TLS_RELATION ].relation_broken , self ._on_tls_relation_broken
95+ self .certs_transfer .on .certificate_available , self ._on_certificate_added
96+ )
97+ self .framework .observe (
98+ self .certs_transfer .on .certificate_removed , self ._on_certificate_removed
7599 )
76- self .framework .observe (self .certs .on .certificate_available , self ._on_certificate_available )
77- self .framework .observe (self .certs .on .certificate_expiring , self ._on_certificate_expiring )
78100
79101 def _on_set_tls_private_key (self , event : ActionEvent ) -> None :
80102 """Set the TLS private key, which will be used for requesting the certificate."""
@@ -93,8 +115,8 @@ def _request_certificate(self, param: Optional[str]):
93115 self .charm .set_secret (SCOPE , "key" , key .decode ("utf-8" ))
94116 self .charm .set_secret (SCOPE , "csr" , csr .decode ("utf-8" ))
95117
96- if self .charm .model .get_relation (TLS_RELATION ):
97- self .certs .request_certificate_creation (certificate_signing_request = csr )
118+ if self .charm .model .get_relation (TLS_CREATION_RELATION ):
119+ self .certs_creation .request_certificate_creation (certificate_signing_request = csr )
98120
99121 @staticmethod
100122 def _parse_tls_file (raw_content : str ) -> bytes :
@@ -117,6 +139,7 @@ def _on_tls_relation_broken(self, event: RelationBrokenEvent) -> None:
117139 self .charm .set_secret (SCOPE , "ca" , None )
118140 self .charm .set_secret (SCOPE , "cert" , None )
119141 self .charm .set_secret (SCOPE , "chain" , None )
142+
120143 if not self .charm .update_config ():
121144 logger .debug ("Cannot update config at this moment" )
122145 event .defer ()
@@ -163,12 +186,52 @@ def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None:
163186 subject = self .charm .get_hostname_by_unit (self .charm .unit .name ),
164187 ** self ._get_sans (),
165188 )
166- self .certs .request_certificate_renewal (
189+ self .certs_creation .request_certificate_renewal (
167190 old_certificate_signing_request = old_csr ,
168191 new_certificate_signing_request = new_csr ,
169192 )
170193 self .charm .set_secret (SCOPE , "csr" , new_csr .decode ("utf-8" ))
171194
195+ def _on_certificate_added (self , event : CertificateAddedEvent ) -> None :
196+ """Enable TLS when TLS certificate is added."""
197+ relation = self .charm .model .get_relation (TLS_TRANSFER_RELATION , event .relation_id )
198+ if relation is None :
199+ logger .error ("Relationship not established anymore." )
200+ return
201+
202+ secret_name = f"ca-{ relation .app .name } "
203+ self .charm .set_secret (SCOPE , secret_name , event .ca )
204+
205+ try :
206+ if not self .charm .push_ca_file_into_workload (secret_name ):
207+ logger .debug ("Cannot push TLS certificates at this moment" )
208+ event .defer ()
209+ return
210+ except (PebbleConnectionError , PathError , ProtocolError , RetryError ) as e :
211+ logger .error ("Cannot push TLS certificates: %r" , e )
212+ event .defer ()
213+ return
214+
215+ def _on_certificate_removed (self , event : CertificateRemovedEvent ) -> None :
216+ """Disable TLS when TLS certificate is removed."""
217+ relation = self .charm .model .get_relation (TLS_TRANSFER_RELATION , event .relation_id )
218+ if relation is None :
219+ logger .error ("Relationship not established anymore." )
220+ return
221+
222+ secret_name = f"ca-{ relation .app .name } "
223+ self .charm .set_secret (SCOPE , secret_name , None )
224+
225+ try :
226+ if not self .charm .clean_ca_file_from_workload (secret_name ):
227+ logger .debug ("Cannot clean CA certificates at this moment" )
228+ event .defer ()
229+ return
230+ except (PebbleConnectionError , PathError , ProtocolError , RetryError ) as e :
231+ logger .error ("Cannot clean CA certificates: %r" , e )
232+ event .defer ()
233+ return
234+
172235 def _get_sans (self ) -> dict :
173236 """Create a list of Subject Alternative Names for a PostgreSQL unit.
174237
0 commit comments