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 }