@@ -320,7 +320,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
320320
321321# Increment this PATCH version before using `charmcraft publish-lib` or reset
322322# to 0 if you are raising the major API version
323- LIBPATCH = 21
323+ LIBPATCH = 22
324324
325325PYDEPS = ["ops>=2.0.0" ]
326326
@@ -763,14 +763,23 @@ def _process_secret_fields(
763763 req_secret_fields : Optional [List [str ]],
764764 impacted_rel_fields : List [str ],
765765 operation : Callable ,
766- second_chance_as_normal_field : bool = True ,
767766 * args ,
768767 ** kwargs ,
769768 ) -> Tuple [Dict [str , str ], Set [str ]]:
770769 """Isolate target secret fields of manipulation, and execute requested operation by Secret Group."""
771770 result = {}
771+
772+ # If the relation started on a databag, we just stay on the databag
773+ # (Rolling upgrades may result in a relation starting on databag, getting secrets enabled on-the-fly)
774+ # self.local_app is sufficient to check (ignored if Requires, never has secrets -- works if Provides)
775+ fallback_to_databag = (
776+ req_secret_fields
777+ and self .local_unit .is_leader ()
778+ and set (req_secret_fields ) & set (relation .data [self .local_app ])
779+ )
780+
772781 normal_fields = set (impacted_rel_fields )
773- if req_secret_fields and self .secrets_enabled :
782+ if req_secret_fields and self .secrets_enabled and not fallback_to_databag :
774783 normal_fields = normal_fields - set (req_secret_fields )
775784 secret_fields = set (impacted_rel_fields ) - set (normal_fields )
776785
@@ -779,8 +788,10 @@ def _process_secret_fields(
779788 for group in secret_fieldnames_grouped :
780789 # operation() should return nothing when all goes well
781790 if group_result := operation (relation , group , secret_fields , * args , ** kwargs ):
782- result .update (group_result )
783- elif second_chance_as_normal_field :
791+ # If "meaningful" data was returned, we take it. (Some 'operation'-s only return success/failure.)
792+ if isinstance (group_result , dict ):
793+ result .update (group_result )
794+ else :
784795 # If it wasn't found as a secret, let's give it a 2nd chance as "normal" field
785796 # Needed when Juju3 Requires meets Juju2 Provider
786797 normal_fields |= set (secret_fieldnames_grouped [group ])
@@ -999,12 +1010,12 @@ def _diff(self, event: RelationChangedEvent) -> Diff:
9991010 @juju_secrets_only
10001011 def _add_relation_secret (
10011012 self , relation : Relation , content : Dict [str , str ], group_mapping : SecretGroup
1002- ) -> Optional [ Secret ] :
1013+ ) -> bool :
10031014 """Add a new Juju Secret that will be registered in the relation databag."""
10041015 secret_field = self ._generate_secret_field_name (group_mapping )
10051016 if relation .data [self .local_app ].get (secret_field ):
10061017 logging .error ("Secret for relation %s already exists, not adding again" , relation .id )
1007- return
1018+ return False
10081019
10091020 label = self ._generate_secret_label (self .relation_name , relation .id , group_mapping )
10101021 secret = self .secrets .add (label , content , relation )
@@ -1013,57 +1024,76 @@ def _add_relation_secret(
10131024 if secret .meta and secret .meta .id :
10141025 relation .data [self .local_app ][secret_field ] = secret .meta .id
10151026
1027+ # Return the content that was added
1028+ return True
1029+
10161030 @juju_secrets_only
10171031 def _update_relation_secret (
10181032 self , relation : Relation , content : Dict [str , str ], group_mapping : SecretGroup
1019- ):
1033+ ) -> bool :
10201034 """Update the contents of an existing Juju Secret, referred in the relation databag."""
10211035 secret = self ._get_relation_secret (relation .id , group_mapping )
10221036
10231037 if not secret :
10241038 logging .error ("Can't update secret for relation %s" , relation .id )
1025- return
1039+ return False
10261040
10271041 old_content = secret .get_content ()
10281042 full_content = copy .deepcopy (old_content )
10291043 full_content .update (content )
10301044 secret .set_content (full_content )
10311045
1046+ # Return True on success
1047+ return True
1048+
10321049 def _add_or_update_relation_secrets (
10331050 self ,
10341051 relation : Relation ,
10351052 group : SecretGroup ,
10361053 secret_fields : Set [str ],
10371054 data : Dict [str , str ],
1038- ) -> None :
1055+ ) -> bool :
10391056 """Update contents for Secret group. If the Secret doesn't exist, create it."""
10401057 secret_content = self ._content_for_secret_group (data , secret_fields , group )
10411058 if self ._get_relation_secret (relation .id , group ):
1042- self ._update_relation_secret (relation , secret_content , group )
1059+ return self ._update_relation_secret (relation , secret_content , group )
10431060 else :
1044- self ._add_relation_secret (relation , secret_content , group )
1061+ return self ._add_relation_secret (relation , secret_content , group )
10451062
10461063 @juju_secrets_only
10471064 def _delete_relation_secret (
10481065 self , relation : Relation , group : SecretGroup , secret_fields : List [str ], fields : List [str ]
1049- ):
1066+ ) -> bool :
10501067 """Update the contents of an existing Juju Secret, referred in the relation databag."""
10511068 secret = self ._get_relation_secret (relation .id , group )
10521069
10531070 if not secret :
1054- logging .error ("Can't update secret for relation %s" , relation .id )
1055- return
1071+ logging .error ("Can't update secret for relation %s" , str ( relation .id ) )
1072+ return False
10561073
10571074 old_content = secret .get_content ()
10581075 new_content = copy .deepcopy (old_content )
10591076 for field in fields :
1060- new_content .pop (field )
1077+ try :
1078+ new_content .pop (field )
1079+ except KeyError :
1080+ logging .error (
1081+ "Non-existing secret was attempted to be removed %s, %s" ,
1082+ str (relation .id ),
1083+ str (field ),
1084+ )
1085+ return False
1086+
10611087 secret .set_content (new_content )
10621088
1089+ # Remove secret from the relation if it's fully gone
10631090 if not new_content :
10641091 field = self ._generate_secret_field_name (group )
10651092 relation .data [self .local_app ].pop (field )
10661093
1094+ # Return the content that was removed
1095+ return True
1096+
10671097 # Mandatory internal overrides
10681098
10691099 @juju_secrets_only
0 commit comments