github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/secrets/secrets.go (about) 1 // Copyright 2022 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package secrets 5 6 import ( 7 "reflect" 8 "sync" 9 10 "github.com/juju/collections/set" 11 "github.com/juju/errors" 12 "github.com/juju/names/v5" 13 14 coresecrets "github.com/juju/juju/core/secrets" 15 "github.com/juju/juju/worker/uniter/hook" 16 "github.com/juju/juju/worker/uniter/remotestate" 17 ) 18 19 // SecretsClient is used by the secrets tracker to access the Juju model. 20 type SecretsClient interface { 21 remotestate.SecretsClient 22 SecretMetadata() ([]coresecrets.SecretOwnerMetadata, error) 23 } 24 25 // Secrets generates storage hooks in response to changes to 26 // storage Secrets, and provides access to information about 27 // storage Secrets to hooks. 28 type Secrets struct { 29 client SecretsClient 30 unitTag names.UnitTag 31 logger Logger 32 33 secretsState *State 34 stateOps *stateOps 35 36 mu sync.Mutex 37 } 38 39 // NewSecrets returns a new secrets tracker. 40 func NewSecrets( 41 client SecretsClient, 42 tag names.UnitTag, 43 rw UnitStateReadWriter, 44 logger Logger, 45 ) (SecretStateTracker, error) { 46 s := &Secrets{ 47 client: client, 48 unitTag: tag, 49 logger: logger, 50 stateOps: NewStateOps(rw), 51 } 52 if err := s.init(); err != nil { 53 return nil, err 54 } 55 return s, nil 56 } 57 58 // init processes initialises the tracker based on the unit 59 // state read from the controller. 60 func (s *Secrets) init() error { 61 existingSecretsState, err := s.stateOps.Read() 62 if err != nil && !errors.IsNotFound(err) { 63 return errors.Annotate(err, "reading secrets state") 64 } 65 s.secretsState = existingSecretsState 66 67 changed := false 68 if len(s.secretsState.ConsumedSecretInfo) > 0 { 69 uris := set.NewStrings() 70 for uri := range s.secretsState.ConsumedSecretInfo { 71 uris.Add(uri) 72 } 73 info, err := s.client.GetConsumerSecretsRevisionInfo(s.unitTag.Id(), uris.SortedValues()) 74 if err != nil { 75 return errors.Annotate(err, "getting consumed secret info") 76 } 77 updated := make(map[string]int) 78 for u, v := range info { 79 updated[u] = v.Revision 80 } 81 changed = !reflect.DeepEqual(updated, s.secretsState.ConsumedSecretInfo) 82 if changed { 83 s.secretsState.ConsumedSecretInfo = updated 84 } 85 } 86 metadata, err := s.client.SecretMetadata() 87 if err != nil { 88 return errors.Annotate(err, "reading secret metadata") 89 } 90 owned := set.NewStrings() 91 for _, md := range metadata { 92 owned.Add(md.Metadata.URI.String()) 93 } 94 for uri := range s.secretsState.SecretObsoleteRevisions { 95 if !owned.Contains(uri) { 96 changed = true 97 delete(s.secretsState.SecretObsoleteRevisions, uri) 98 } 99 } 100 if !changed { 101 return nil 102 } 103 104 return s.stateOps.Write(s.secretsState) 105 } 106 107 // ConsumedSecretRevision implements SecretStateTracker. 108 func (s *Secrets) ConsumedSecretRevision(uri string) int { 109 s.mu.Lock() 110 defer s.mu.Unlock() 111 112 return s.secretsState.ConsumedSecretInfo[uri] 113 } 114 115 // SecretObsoleteRevisions implements SecretStateTracker. 116 func (s *Secrets) SecretObsoleteRevisions(uri string) []int { 117 s.mu.Lock() 118 defer s.mu.Unlock() 119 120 revs := s.secretsState.SecretObsoleteRevisions[uri] 121 result := make([]int, len(revs)) 122 copy(result, revs) 123 return result 124 } 125 126 // PrepareHook implements SecretStateTracker. 127 func (s *Secrets) PrepareHook(hi hook.Info) error { 128 if !hi.Kind.IsSecret() { 129 return errors.Errorf("not a secret hook: %#v", hi) 130 } 131 132 return nil 133 } 134 135 // CommitHook implements SecretStateTracker. 136 func (s *Secrets) CommitHook(hi hook.Info) error { 137 if !hi.Kind.IsSecret() { 138 return errors.Errorf("not a secret hook: %#v", hi) 139 } 140 141 s.mu.Lock() 142 defer s.mu.Unlock() 143 144 s.secretsState.UpdateStateForHook(hi) 145 if err := s.stateOps.Write(s.secretsState); err != nil { 146 return err 147 } 148 return nil 149 } 150 151 // SecretsRemoved implements SecretStateTracker. 152 func (s *Secrets) SecretsRemoved(uris []string) error { 153 s.mu.Lock() 154 defer s.mu.Unlock() 155 156 for _, uri := range uris { 157 delete(s.secretsState.ConsumedSecretInfo, uri) 158 delete(s.secretsState.SecretObsoleteRevisions, uri) 159 } 160 if err := s.stateOps.Write(s.secretsState); err != nil { 161 return err 162 } 163 return nil 164 } 165 166 // Report provides information for the engine report. 167 func (s *Secrets) Report() map[string]interface{} { 168 s.mu.Lock() 169 defer s.mu.Unlock() 170 171 result := make(map[string]interface{}) 172 obsolete := make(map[string][]int) 173 for u, v := range s.secretsState.SecretObsoleteRevisions { 174 rCopy := make([]int, len(v)) 175 copy(rCopy, v) 176 obsolete[u] = rCopy 177 } 178 result["obsolete-revisions"] = obsolete 179 180 consumed := make(map[string]int) 181 for u, v := range s.secretsState.ConsumedSecretInfo { 182 consumed[u] = v 183 } 184 result["consumed-revisions"] = consumed 185 186 return result 187 }