go.ligato.io/vpp-agent/v3@v3.5.0/pkg/models/models.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package models 16 17 import ( 18 "fmt" 19 "path" 20 "reflect" 21 "strings" 22 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/reflect/protoreflect" 25 "google.golang.org/protobuf/runtime/protoimpl" 26 "google.golang.org/protobuf/types/dynamicpb" 27 ) 28 29 // Register registers model in DefaultRegistry. 30 func Register(pb proto.Message, spec Spec, opts ...ModelOption) KnownModel { 31 model, err := DefaultRegistry.Register(pb, spec, opts...) 32 if err != nil { 33 panic(err) 34 } 35 return model 36 } 37 38 // RegisterRemote registers remotely known model in given RemoteRegistry 39 func RegisterRemote(remoteModel *ModelInfo, remoteRegistry *RemoteRegistry) KnownModel { 40 model, err := remoteRegistry.Register(remoteModel, ToSpec(remoteModel.Spec)) 41 if err != nil { 42 panic(err) 43 } 44 return model 45 } 46 47 // RegisteredModels returns models registered in the DefaultRegistry. 48 func RegisteredModels() []KnownModel { 49 return DefaultRegistry.RegisteredModels() 50 } 51 52 // GetModel returns registered model for given model name. 53 func GetModel(name string) (KnownModel, error) { 54 return GetModelFromRegistry(name, DefaultRegistry) 55 } 56 57 // GetModelFromRegistry returns registered model in given registry for given model name. 58 func GetModelFromRegistry(name string, modelRegistry Registry) (KnownModel, error) { 59 return modelRegistry.GetModel(name) 60 } 61 62 // GetModelFor returns model registered in DefaultRegistry for given proto message. 63 func GetModelFor(x proto.Message) (KnownModel, error) { 64 return GetModelFromRegistryFor(x, DefaultRegistry) 65 } 66 67 // GetModelFromRegistryFor returns model registered in modelRegistry for given proto message 68 func GetModelFromRegistryFor(x proto.Message, modelRegistry Registry) (KnownModel, error) { 69 return modelRegistry.GetModelFor(x) 70 } 71 72 // GetModelForKey returns model registered in DefaultRegistry which matches key. 73 func GetModelForKey(key string) (KnownModel, error) { 74 return DefaultRegistry.GetModelForKey(key) 75 } 76 77 // Key is a helper for the GetKey which panics on errors. 78 func Key(x proto.Message) string { 79 key, err := GetKey(x) 80 if err != nil { 81 panic(err) 82 } 83 return key 84 } 85 86 // Name is a helper for the GetName which panics on errors. 87 func Name(x proto.Message) string { 88 name, err := GetName(x) 89 if err != nil { 90 panic(err) 91 } 92 return name 93 } 94 95 // GetKey returns complete key for given model, 96 // including key prefix defined by model specification. 97 // It returns error if given model is not registered. 98 func GetKey(x proto.Message) (string, error) { 99 return GetKeyUsingModelRegistry(x, DefaultRegistry) 100 } 101 102 // GetKeyUsingModelRegistry returns complete key for given model from given model registry, 103 // including key prefix defined by model specification. 104 // It returns error if given model is not registered. 105 func GetKeyUsingModelRegistry(message proto.Message, modelRegistry Registry) (string, error) { 106 // find model for message 107 model, err := GetModelFromRegistryFor(message, modelRegistry) 108 if err != nil { 109 return "", fmt.Errorf("cannot find known model "+ 110 "for message (while getting key for model) due to: %w (message = %+v)", err, message) 111 } 112 113 // compute Item.ID.Name 114 name, err := model.InstanceName(message) 115 if err != nil { 116 return "", fmt.Errorf("cannot compute model instance name due to: %v (message %+v)", err, message) 117 } 118 119 key := path.Join(model.KeyPrefix(), name) 120 return key, nil 121 } 122 123 // GetName returns instance name for given model. 124 // It returns error if given model is not registered. 125 func GetName(x proto.Message) (string, error) { 126 model, err := GetModelFor(x) 127 if err != nil { 128 return "", err 129 } 130 name, err := model.InstanceName(x) 131 if err != nil { 132 return "", err 133 } 134 return name, nil 135 } 136 137 // keyPrefix computes correct key prefix from given model. It 138 // handles correctly the case when name suffix of the key is empty 139 // (no template name -> key prefix does not end with "/") 140 func keyPrefix(modelSpec Spec, hasTemplateName bool) string { 141 keyPrefix := modelSpec.KeyPrefix() 142 if !hasTemplateName { 143 keyPrefix = strings.TrimSuffix(keyPrefix, "/") 144 } 145 return keyPrefix 146 } 147 148 // DynamicLocallyKnownMessageToGeneratedMessage converts locally registered/known proto dynamic message to 149 // corresponding statically generated proto message. This function will fail when there is no registration 150 // of statically-generated proto message, i.e. dynamic message refers to remotely known model. 151 // This conversion method should help handling dynamic proto messages in mostly protoc-generated proto message 152 // oriented codebase (i.e. help for type conversions to named, help handle missing data fields as seen 153 // in generated proto messages,...) 154 func DynamicLocallyKnownMessageToGeneratedMessage(dynamicMessage *dynamicpb.Message) (proto.Message, error) { 155 // get go type of statically generated proto message corresponding to locally known dynamic message 156 model, err := GetModelFor(dynamicMessage) 157 if err != nil { 158 return nil, fmt.Errorf("can't get model "+ 159 "for dynamic message due to: %w (message=%v)", err, dynamicMessage) 160 } 161 goType := model.LocalGoType() // only for locally known models will return meaningful go type 162 if goType == nil { 163 return nil, fmt.Errorf("dynamic messages for remote models are not supported due to "+ 164 "not available go type of statically generated proto message (dynamic message=%v)", dynamicMessage) 165 } 166 167 // create empty statically-generated proto message of the same type as it was used for registration 168 var registeredGoType interface{} 169 if goType.Kind() == reflect.Ptr { 170 registeredGoType = reflect.New(goType.Elem()).Interface() 171 } else { 172 registeredGoType = reflect.Zero(goType).Interface() 173 } 174 175 message := protoMessageOf(registeredGoType) 176 177 // fill empty statically-generated proto message with data from its dynamic proto message counterpart 178 // (alternative approach to this is marshalling dynamicMessage to json and unmarshalling it back to message) 179 proto.Merge(message, dynamicMessage) 180 181 return message, nil 182 } 183 184 func protoMessageOf(m interface{}) protoreflect.ProtoMessage { 185 return protoimpl.X.ProtoMessageV2Of(m) 186 }