github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/annotations/client.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package annotations
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  
    10  	apiservererrors "github.com/juju/juju/apiserver/errors"
    11  	"github.com/juju/juju/apiserver/facade"
    12  	"github.com/juju/juju/core/permission"
    13  	"github.com/juju/juju/rpc/params"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  var getState = func(st *state.State, m *state.Model) annotationAccess {
    18  	return stateShim{st, m}
    19  }
    20  
    21  // Annotations defines the methods on the service API end point.
    22  type Annotations interface {
    23  	Get(args params.Entities) params.AnnotationsGetResults
    24  	Set(args params.AnnotationsSet) params.ErrorResults
    25  }
    26  
    27  // API implements the service interface and is the concrete
    28  // implementation of the api end point.
    29  type API struct {
    30  	access     annotationAccess
    31  	authorizer facade.Authorizer
    32  }
    33  
    34  func (api *API) checkCanRead() error {
    35  	return api.authorizer.HasPermission(permission.ReadAccess, api.access.ModelTag())
    36  }
    37  
    38  func (api *API) checkCanWrite() error {
    39  	return api.authorizer.HasPermission(permission.WriteAccess, api.access.ModelTag())
    40  }
    41  
    42  // Get returns annotations for given entities.
    43  // If annotations cannot be retrieved for a given entity, an error is returned.
    44  // Each entity is treated independently and, hence, will fail or succeed independently.
    45  func (api *API) Get(args params.Entities) params.AnnotationsGetResults {
    46  	if err := api.checkCanRead(); err != nil {
    47  		result := make([]params.AnnotationsGetResult, len(args.Entities))
    48  		for i := range result {
    49  			result[i].Error = params.ErrorResult{Error: apiservererrors.ServerError(err)}
    50  		}
    51  		return params.AnnotationsGetResults{Results: result}
    52  	}
    53  
    54  	entityResults := []params.AnnotationsGetResult{}
    55  	for _, entity := range args.Entities {
    56  		anEntityResult := params.AnnotationsGetResult{EntityTag: entity.Tag}
    57  		if annts, err := api.getEntityAnnotations(entity.Tag); err != nil {
    58  			anEntityResult.Error = params.ErrorResult{annotateError(err, entity.Tag, "getting")}
    59  		} else {
    60  			anEntityResult.Annotations = annts
    61  		}
    62  		entityResults = append(entityResults, anEntityResult)
    63  	}
    64  	return params.AnnotationsGetResults{Results: entityResults}
    65  }
    66  
    67  // Set stores annotations for given entities
    68  func (api *API) Set(args params.AnnotationsSet) params.ErrorResults {
    69  	if err := api.checkCanWrite(); err != nil {
    70  		errorResults := make([]params.ErrorResult, len(args.Annotations))
    71  		for i := range errorResults {
    72  			errorResults[i].Error = apiservererrors.ServerError(err)
    73  		}
    74  		return params.ErrorResults{Results: errorResults}
    75  	}
    76  	setErrors := []params.ErrorResult{}
    77  	for _, entityAnnotation := range args.Annotations {
    78  		err := api.setEntityAnnotations(entityAnnotation.EntityTag, entityAnnotation.Annotations)
    79  		if err != nil {
    80  			setErrors = append(setErrors,
    81  				params.ErrorResult{Error: annotateError(err, entityAnnotation.EntityTag, "setting")})
    82  		}
    83  	}
    84  	return params.ErrorResults{Results: setErrors}
    85  }
    86  
    87  func annotateError(err error, tag, op string) *params.Error {
    88  	return apiservererrors.ServerError(
    89  		errors.Trace(
    90  			errors.Annotatef(
    91  				err, "while %v annotations to %q", op, tag)))
    92  }
    93  
    94  func (api *API) getEntityAnnotations(entityTag string) (map[string]string, error) {
    95  	tag, err := names.ParseTag(entityTag)
    96  	if err != nil {
    97  		return nil, errors.Trace(err)
    98  	}
    99  	entity, err := api.findEntity(tag)
   100  	if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  	annotations, err := api.access.Annotations(entity)
   104  	if err != nil {
   105  		return nil, errors.Trace(err)
   106  	}
   107  	return annotations, nil
   108  }
   109  
   110  func (api *API) findEntity(tag names.Tag) (state.GlobalEntity, error) {
   111  	entity0, err := api.access.FindEntity(tag)
   112  	if err != nil {
   113  		if errors.IsNotFound(err) {
   114  			return nil, apiservererrors.ErrPerm
   115  		}
   116  		return nil, err
   117  	}
   118  	entity, ok := entity0.(state.GlobalEntity)
   119  	if !ok {
   120  		return nil, apiservererrors.NotSupportedError(tag, "annotations")
   121  	}
   122  	return entity, nil
   123  }
   124  
   125  func (api *API) setEntityAnnotations(entityTag string, annotations map[string]string) error {
   126  	tag, err := names.ParseTag(entityTag)
   127  	if err != nil {
   128  		return errors.Trace(err)
   129  	}
   130  	entity, err := api.findEntity(tag)
   131  	if err != nil {
   132  		return errors.Trace(err)
   133  	}
   134  	return api.access.SetAnnotations(entity, annotations)
   135  }