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 = &params.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 = &params.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 = &params.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 = &params.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  }