12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
- """A library for communicating with the S3 credentials providers and consumers.
15
+ r """A library for communicating with the S3 credentials providers and consumers.
16
16
17
17
This library provides the relevant interface code implementing the communication
18
18
specification for fetching, retrieving, triggering, and responding to events related to
@@ -113,23 +113,21 @@ def _on_credential_gone(self, event: CredentialsGoneEvent):
113
113
import json
114
114
import logging
115
115
from collections import namedtuple
116
- from typing import Dict , List , Optional
116
+ from typing import Dict , List , Optional , Union
117
117
118
118
import ops .charm
119
119
import ops .framework
120
120
import ops .model
121
121
from ops .charm import (
122
122
CharmBase ,
123
123
CharmEvents ,
124
- EventSource ,
125
- Object ,
126
- ObjectEvents ,
127
124
RelationBrokenEvent ,
128
125
RelationChangedEvent ,
129
126
RelationEvent ,
130
127
RelationJoinedEvent ,
131
128
)
132
- from ops .model import Relation
129
+ from ops .framework import EventSource , Object , ObjectEvents
130
+ from ops .model import Application , Relation , RelationDataContent , Unit
133
131
134
132
# The unique Charmhub library identifier, never change it
135
133
LIBID = "fca396f6254246c9bfa565b1f85ab528"
@@ -139,7 +137,7 @@ def _on_credential_gone(self, event: CredentialsGoneEvent):
139
137
140
138
# Increment this PATCH version before using `charmcraft publish-lib` or reset
141
139
# to 0 if you are raising the major API version
142
- LIBPATCH = 2
140
+ LIBPATCH = 4
143
141
144
142
logger = logging .getLogger (__name__ )
145
143
@@ -152,7 +150,7 @@ def _on_credential_gone(self, event: CredentialsGoneEvent):
152
150
deleted - key that were deleted"""
153
151
154
152
155
- def diff (event : RelationChangedEvent , bucket : str ) -> Diff :
153
+ def diff (event : RelationChangedEvent , bucket : Union [ Unit , Application ] ) -> Diff :
156
154
"""Retrieves the diff of the data in the relation changed databag.
157
155
158
156
Args:
@@ -166,9 +164,11 @@ def diff(event: RelationChangedEvent, bucket: str) -> Diff:
166
164
# Retrieve the old data from the data key in the application relation databag.
167
165
old_data = json .loads (event .relation .data [bucket ].get ("data" , "{}" ))
168
166
# Retrieve the new data from the event relation databag.
169
- new_data = {
170
- key : value for key , value in event .relation .data [event .app ].items () if key != "data"
171
- }
167
+ new_data = (
168
+ {key : value for key , value in event .relation .data [event .app ].items () if key != "data" }
169
+ if event .app
170
+ else {}
171
+ )
172
172
173
173
# These are the keys that were added to the databag and triggered this event.
174
174
added = new_data .keys () - old_data .keys ()
@@ -193,7 +193,10 @@ class BucketEvent(RelationEvent):
193
193
@property
194
194
def bucket (self ) -> Optional [str ]:
195
195
"""Returns the bucket was requested."""
196
- return self .relation .data [self .relation .app ].get ("bucket" )
196
+ if not self .relation .app :
197
+ return None
198
+
199
+ return self .relation .data [self .relation .app ].get ("bucket" , "" )
197
200
198
201
199
202
class CredentialRequestedEvent (BucketEvent ):
@@ -209,7 +212,7 @@ class S3CredentialEvents(CharmEvents):
209
212
class S3Provider (Object ):
210
213
"""A provider handler for communicating S3 credentials to consumers."""
211
214
212
- on = S3CredentialEvents ()
215
+ on = S3CredentialEvents () # pyright: ignore [reportGeneralTypeIssues]
213
216
214
217
def __init__ (
215
218
self ,
@@ -232,7 +235,9 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
232
235
diff = self ._diff (event )
233
236
# emit on credential requested if bucket is provided by the requirer application
234
237
if "bucket" in diff .added :
235
- self .on .credentials_requested .emit (event .relation , app = event .app , unit = event .unit )
238
+ getattr (self .on , "credentials_requested" ).emit (
239
+ event .relation , app = event .app , unit = event .unit
240
+ )
236
241
237
242
def _load_relation_data (self , raw_relation_data : dict ) -> dict :
238
243
"""Loads relation data from the relation data bag.
@@ -242,7 +247,7 @@ def _load_relation_data(self, raw_relation_data: dict) -> dict:
242
247
Returns:
243
248
dict: Relation data in dict format.
244
249
"""
245
- connection_data = dict ()
250
+ connection_data = {}
246
251
for key in raw_relation_data :
247
252
try :
248
253
connection_data [key ] = json .loads (raw_relation_data [key ])
@@ -309,9 +314,11 @@ def fetch_relation_data(self) -> dict:
309
314
"""
310
315
data = {}
311
316
for relation in self .relations :
312
- data [relation .id ] = {
313
- key : value for key , value in relation .data [relation .app ].items () if key != "data"
314
- }
317
+ data [relation .id ] = (
318
+ {key : value for key , value in relation .data [relation .app ].items () if key != "data" }
319
+ if relation .app
320
+ else {}
321
+ )
315
322
return data
316
323
317
324
def update_connection_info (self , relation_id : int , connection_data : dict ) -> None :
@@ -493,46 +500,73 @@ class S3Event(RelationEvent):
493
500
@property
494
501
def bucket (self ) -> Optional [str ]:
495
502
"""Returns the bucket name."""
503
+ if not self .relation .app :
504
+ return None
505
+
496
506
return self .relation .data [self .relation .app ].get ("bucket" )
497
507
498
508
@property
499
509
def access_key (self ) -> Optional [str ]:
500
510
"""Returns the access key."""
511
+ if not self .relation .app :
512
+ return None
513
+
501
514
return self .relation .data [self .relation .app ].get ("access-key" )
502
515
503
516
@property
504
517
def secret_key (self ) -> Optional [str ]:
505
518
"""Returns the secret key."""
519
+ if not self .relation .app :
520
+ return None
521
+
506
522
return self .relation .data [self .relation .app ].get ("secret-key" )
507
523
508
524
@property
509
525
def path (self ) -> Optional [str ]:
510
526
"""Returns the path where data can be stored."""
527
+ if not self .relation .app :
528
+ return None
529
+
511
530
return self .relation .data [self .relation .app ].get ("path" )
512
531
513
532
@property
514
533
def endpoint (self ) -> Optional [str ]:
515
534
"""Returns the endpoint address."""
535
+ if not self .relation .app :
536
+ return None
537
+
516
538
return self .relation .data [self .relation .app ].get ("endpoint" )
517
539
518
540
@property
519
541
def region (self ) -> Optional [str ]:
520
542
"""Returns the region."""
543
+ if not self .relation .app :
544
+ return None
545
+
521
546
return self .relation .data [self .relation .app ].get ("region" )
522
547
523
548
@property
524
549
def s3_uri_style (self ) -> Optional [str ]:
525
550
"""Returns the s3 uri style."""
551
+ if not self .relation .app :
552
+ return None
553
+
526
554
return self .relation .data [self .relation .app ].get ("s3-uri-style" )
527
555
528
556
@property
529
557
def storage_class (self ) -> Optional [str ]:
530
558
"""Returns the storage class name."""
559
+ if not self .relation .app :
560
+ return None
561
+
531
562
return self .relation .data [self .relation .app ].get ("storage-class" )
532
563
533
564
@property
534
565
def tls_ca_chain (self ) -> Optional [List [str ]]:
535
566
"""Returns the TLS CA chain."""
567
+ if not self .relation .app :
568
+ return None
569
+
536
570
tls_ca_chain = self .relation .data [self .relation .app ].get ("tls-ca-chain" )
537
571
if tls_ca_chain is not None :
538
572
return json .loads (tls_ca_chain )
@@ -541,11 +575,17 @@ def tls_ca_chain(self) -> Optional[List[str]]:
541
575
@property
542
576
def s3_api_version (self ) -> Optional [str ]:
543
577
"""Returns the S3 API version."""
578
+ if not self .relation .app :
579
+ return None
580
+
544
581
return self .relation .data [self .relation .app ].get ("s3-api-version" )
545
582
546
583
@property
547
584
def attributes (self ) -> Optional [List [str ]]:
548
585
"""Returns the attributes."""
586
+ if not self .relation .app :
587
+ return None
588
+
549
589
attributes = self .relation .data [self .relation .app ].get ("attributes" )
550
590
if attributes is not None :
551
591
return json .loads (attributes )
@@ -573,9 +613,11 @@ class S3CredentialRequiresEvents(ObjectEvents):
573
613
class S3Requirer (Object ):
574
614
"""Requires-side of the s3 relation."""
575
615
576
- on = S3CredentialRequiresEvents ()
616
+ on = S3CredentialRequiresEvents () # pyright: ignore[reportGeneralTypeIssues]
577
617
578
- def __init__ (self , charm : ops .charm .CharmBase , relation_name : str , bucket_name : str = None ):
618
+ def __init__ (
619
+ self , charm : ops .charm .CharmBase , relation_name : str , bucket_name : Optional [str ] = None
620
+ ):
579
621
"""Manager of the s3 client relations."""
580
622
super ().__init__ (charm , relation_name )
581
623
@@ -658,15 +700,15 @@ def update_connection_info(self, relation_id: int, connection_data: dict) -> Non
658
700
relation .data [self .local_app ].update (updated_connection_data )
659
701
logger .debug (f"Updated S3 credentials: { updated_connection_data } " )
660
702
661
- def _load_relation_data (self , raw_relation_data : dict ) -> dict :
703
+ def _load_relation_data (self , raw_relation_data : RelationDataContent ) -> Dict [ str , str ] :
662
704
"""Loads relation data from the relation data bag.
663
705
664
706
Args:
665
707
raw_relation_data: Relation data from the databag
666
708
Returns:
667
709
dict: Relation data in dict format.
668
710
"""
669
- connection_data = dict ()
711
+ connection_data = {}
670
712
for key in raw_relation_data :
671
713
try :
672
714
connection_data [key ] = json .loads (raw_relation_data [key ])
@@ -700,22 +742,25 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
700
742
missing_options .append (configuration_option )
701
743
# emit credential change event only if all mandatory fields are present
702
744
if contains_required_options :
703
- self .on .credentials_changed .emit (event .relation , app = event .app , unit = event .unit )
745
+ getattr (self .on , "credentials_changed" ).emit (
746
+ event .relation , app = event .app , unit = event .unit
747
+ )
704
748
else :
705
749
logger .warning (
706
750
f"Some mandatory fields: { missing_options } are not present, do not emit credential change event!"
707
751
)
708
752
709
- def get_s3_connection_info (self ) -> Dict :
753
+ def get_s3_connection_info (self ) -> Dict [ str , str ] :
710
754
"""Return the s3 credentials as a dictionary."""
711
- relation = self .charm .model .get_relation (self .relation_name )
712
- if not relation :
713
- return {}
714
- return self ._load_relation_data (relation .data [relation .app ])
755
+ for relation in self .relations :
756
+ if relation and relation .app :
757
+ return self ._load_relation_data (relation .data [relation .app ])
758
+
759
+ return {}
715
760
716
761
def _on_relation_broken (self , event : RelationBrokenEvent ) -> None :
717
762
"""Notify the charm about a broken S3 credential store relation."""
718
- self .on . credentials_gone .emit (event .relation , app = event .app , unit = event .unit )
763
+ getattr ( self .on , " credentials_gone" ) .emit (event .relation , app = event .app , unit = event .unit )
719
764
720
765
@property
721
766
def relations (self ) -> List [Relation ]:
0 commit comments