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  }