github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/usersecretsdrain/drain.go (about) 1 // Copyright 2023 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package usersecretsdrain 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 10 commonsecrets "github.com/juju/juju/apiserver/common/secrets" 11 apiservererrors "github.com/juju/juju/apiserver/errors" 12 coresecrets "github.com/juju/juju/core/secrets" 13 "github.com/juju/juju/rpc/params" 14 "github.com/juju/juju/secrets" 15 "github.com/juju/juju/secrets/provider" 16 ) 17 18 var logger = loggo.GetLogger("juju.apiserver.usersecretsdrain") 19 20 // SecretsDrainAPI is the implementation for the SecretsDrain facade. 21 type SecretsDrainAPI struct { 22 *commonsecrets.SecretsDrainAPI 23 24 secretsState SecretsState 25 26 drainConfigGetter commonsecrets.BackendDrainConfigGetter 27 backendConfigGetter commonsecrets.BackendConfigGetter 28 } 29 30 // GetSecretBackendConfigs gets the config needed to create a client to secret backends for the drain worker. 31 func (s *SecretsDrainAPI) GetSecretBackendConfigs(arg params.SecretBackendArgs) (params.SecretBackendConfigResults, error) { 32 if len(arg.BackendIDs) > 1 { 33 return params.SecretBackendConfigResults{}, errors.Errorf("Maximumly only one backend ID can be specified for drain") 34 } 35 var backendID string 36 if len(arg.BackendIDs) == 1 { 37 backendID = arg.BackendIDs[0] 38 } 39 results := params.SecretBackendConfigResults{ 40 Results: make(map[string]params.SecretBackendConfigResult, 1), 41 } 42 cfgInfo, err := s.drainConfigGetter(backendID) 43 if err != nil { 44 return results, errors.Trace(err) 45 } 46 if len(cfgInfo.Configs) == 0 { 47 return results, errors.NotFoundf("no secret backends available") 48 } 49 results.ActiveID = cfgInfo.ActiveID 50 for id, cfg := range cfgInfo.Configs { 51 results.Results[id] = params.SecretBackendConfigResult{ 52 ControllerUUID: cfg.ControllerUUID, 53 ModelUUID: cfg.ModelUUID, 54 ModelName: cfg.ModelName, 55 Draining: true, 56 Config: params.SecretBackendConfig{ 57 BackendType: cfg.BackendType, 58 Params: cfg.Config, 59 }, 60 } 61 } 62 return results, nil 63 } 64 65 // GetSecretContentInfo returns the secret values for the specified secrets. 66 func (s *SecretsDrainAPI) GetSecretContentInfo(args params.GetSecretContentArgs) (params.SecretContentResults, error) { 67 result := params.SecretContentResults{ 68 Results: make([]params.SecretContentResult, len(args.Args)), 69 } 70 for i, arg := range args.Args { 71 content, backend, draining, err := s.getSecretContent(arg) 72 if err != nil { 73 result.Results[i].Error = apiservererrors.ServerError(err) 74 continue 75 } 76 contentParams := params.SecretContentParams{} 77 if content.ValueRef != nil { 78 contentParams.ValueRef = ¶ms.SecretValueRef{ 79 BackendID: content.ValueRef.BackendID, 80 RevisionID: content.ValueRef.RevisionID, 81 } 82 } 83 if content.SecretValue != nil { 84 contentParams.Data = content.SecretValue.EncodedValues() 85 } 86 result.Results[i].Content = contentParams 87 if backend != nil { 88 result.Results[i].BackendConfig = ¶ms.SecretBackendConfigResult{ 89 ControllerUUID: backend.ControllerUUID, 90 ModelUUID: backend.ModelUUID, 91 ModelName: backend.ModelName, 92 Draining: draining, 93 Config: params.SecretBackendConfig{ 94 BackendType: backend.BackendType, 95 Params: backend.Config, 96 }, 97 } 98 } 99 } 100 return result, nil 101 } 102 103 func (s *SecretsDrainAPI) getSecretContent(arg params.GetSecretContentArg) ( 104 *secrets.ContentParams, *provider.ModelBackendConfig, bool, error, 105 ) { 106 if arg.URI == "" { 107 return nil, nil, false, errors.NewNotValid(nil, "empty URI") 108 } 109 110 uri, err := coresecrets.ParseURI(arg.URI) 111 if err != nil { 112 return nil, nil, false, errors.Trace(err) 113 } 114 logger.Debugf("getting secret content for: %s", uri) 115 116 md, err := s.secretsState.GetSecret(uri) 117 if err != nil { 118 return nil, nil, false, errors.Trace(err) 119 } 120 121 val, valueRef, err := s.secretsState.GetSecretValue(md.URI, md.LatestRevision) 122 if err != nil { 123 return nil, nil, false, errors.Trace(err) 124 } 125 content := &secrets.ContentParams{SecretValue: val, ValueRef: valueRef} 126 if content.ValueRef == nil { 127 // Internal secret. 128 return content, nil, false, errors.Trace(err) 129 } 130 // Get backend config for external secret. 131 backend, draining, err := s.getBackend(content.ValueRef.BackendID) 132 return content, backend, draining, errors.Trace(err) 133 } 134 135 func (s *SecretsDrainAPI) getBackend(backendID string) (*provider.ModelBackendConfig, bool, error) { 136 cfgInfo, err := s.backendConfigGetter([]string{backendID}, false) 137 if err != nil { 138 return nil, false, errors.Trace(err) 139 } 140 cfg, ok := cfgInfo.Configs[backendID] 141 if ok { 142 return &provider.ModelBackendConfig{ 143 ControllerUUID: cfg.ControllerUUID, 144 ModelUUID: cfg.ModelUUID, 145 ModelName: cfg.ModelName, 146 BackendConfig: provider.BackendConfig{ 147 BackendType: cfg.BackendType, 148 Config: cfg.Config, 149 }, 150 }, backendID != cfgInfo.ActiveID, nil 151 } 152 return nil, false, errors.NotFoundf("secret backend %q", backendID) 153 } 154 155 // GetSecretRevisionContentInfo returns the secret values for the specified secret revisions. 156 func (s *SecretsDrainAPI) GetSecretRevisionContentInfo(arg params.SecretRevisionArg) (params.SecretContentResults, error) { 157 result := params.SecretContentResults{ 158 Results: make([]params.SecretContentResult, len(arg.Revisions)), 159 } 160 uri, err := coresecrets.ParseURI(arg.URI) 161 if err != nil { 162 return params.SecretContentResults{}, errors.Trace(err) 163 } 164 165 for i, rev := range arg.Revisions { 166 val, valueRef, err := s.secretsState.GetSecretValue(uri, rev) 167 if err != nil { 168 result.Results[i].Error = apiservererrors.ServerError(err) 169 continue 170 } 171 contentParams := params.SecretContentParams{} 172 if valueRef != nil { 173 contentParams.ValueRef = ¶ms.SecretValueRef{ 174 BackendID: valueRef.BackendID, 175 RevisionID: valueRef.RevisionID, 176 } 177 backend, draining, err := s.getBackend(valueRef.BackendID) 178 if err != nil { 179 result.Results[i].Error = apiservererrors.ServerError(err) 180 continue 181 } 182 result.Results[i].BackendConfig = ¶ms.SecretBackendConfigResult{ 183 ControllerUUID: backend.ControllerUUID, 184 ModelUUID: backend.ModelUUID, 185 ModelName: backend.ModelName, 186 Draining: draining, 187 Config: params.SecretBackendConfig{ 188 BackendType: backend.BackendType, 189 Params: backend.Config, 190 }, 191 } 192 } 193 if val != nil { 194 contentParams.Data = val.EncodedValues() 195 } 196 result.Results[i].Content = contentParams 197 } 198 return result, nil 199 }