go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/validation.go (about) 1 // Copyright (c) 2020 Pantheon.tech 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 kvscheduler 16 17 import ( 18 "github.com/go-errors/errors" 19 "google.golang.org/protobuf/proto" 20 "google.golang.org/protobuf/types/dynamicpb" 21 22 "go.ligato.io/vpp-agent/v3/pkg/models" 23 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 24 ) 25 26 // ValidateSemantically validates given proto messages according to semantic validation(KVDescriptor.Validate) 27 // from registered KVDescriptors. If all locally known messages are valid, nil is returned. If some locally known 28 // messages are invalid, kvscheduler.MessageValidationErrors is returned. In any other case, error is returned. 29 // 30 // Usage of dynamic proto messages (dynamicpb.Message) described by remotely known models is not supported. 31 // The reason for this is that the KVDescriptors can validate only statically generated proto messages and 32 // remotely retrieved dynamic proto messages can't be converted to such proto messages (there are 33 // no locally available statically generated proto models). 34 func (s *Scheduler) ValidateSemantically(messages []proto.Message) error { 35 s.txnLock.Lock() 36 defer s.txnLock.Unlock() 37 38 invalidMessageErrors := make([]*api.InvalidMessageError, 0) 39 for _, message := range messages { 40 originalMessage := message 41 42 // if needed, convert dynamic proto message to statically generated proto message 43 // (validators in descriptors can validate only statically generated proto messages) 44 if dynamicMessage, isDyn := message.(*dynamicpb.Message); isDyn { 45 model, err := models.GetModelFor(message) 46 if err != nil { 47 return errors.Errorf("can't get model for message due to: %v (message=%v)", err, message) 48 } 49 goType := model.LocalGoType() // only for locally known models will return meaningful go type 50 if goType == nil { 51 s.Log.Debug("instance of %s doesn't have local go type and will be skipped from validation "+ 52 "using KVDescriptor.Validate (dynamic message=%v)", model.ProtoName(), message) 53 continue 54 } 55 message, err = models.DynamicLocallyKnownMessageToGeneratedMessage(dynamicMessage) 56 if err != nil { 57 return errors.Errorf("can't convert dynamic message to statically generated message "+ 58 "due to: %v (dynamic message=%v)", err, dynamicMessage) 59 } 60 } 61 62 // get descriptor and key for given message 63 key, err := models.GetKey(message) 64 if err != nil { 65 return errors.Errorf("can't get message key due to: %v (message=%v)", err, message) 66 } 67 descriptor := s.registry.GetDescriptorForKey(key) 68 if descriptor == nil { 69 s.Log.Debug("Skipping validation for proto message key %s "+ 70 "due to missing descriptor (proto message: %v)", key, message) 71 continue 72 } 73 descHandler := newDescriptorHandler(descriptor) 74 75 // validate and collect validation errors 76 if err = descHandler.validate(key, message); err != nil { 77 if ivError, ok := err.(*api.InvalidValueError); ok { 78 // only InvalidValueErrors are supposed to describe data invalidity 79 invalidMessageErrors = append(invalidMessageErrors, 80 api.NewInvalidMessageError(originalMessage, ivError, nil)) 81 } else { 82 s.Log.Warn("Validation of KVDescriptor %s returns error that is not InvalidValueError. "+ 83 "Using conversion to InvalidValueError, but the invalid fields will be unknown.", descriptor.Name) 84 invalidMessageErrors = append(invalidMessageErrors, api.NewInvalidMessageError( 85 originalMessage, api.NewInvalidValueError(err, ""), nil)) 86 } 87 } 88 89 // validate also derived values 90 for _, derivedValue := range descHandler.derivedValues(key, message) { 91 descriptor = s.registry.GetDescriptorForKey(derivedValue.Key) 92 if descriptor == nil { 93 s.Log.Debug("Skipping validation for proto message's derived value key %s "+ 94 "due to missing descriptor (proto message: %v, derived value proto message: %v)", 95 derivedValue.Key, message, derivedValue.Value) 96 continue 97 } 98 descHandler = newDescriptorHandler(descriptor) 99 if err = descHandler.validate(derivedValue.Key, derivedValue.Value); err != nil { 100 if ivError, ok := err.(*api.InvalidValueError); ok { 101 // only InvalidValueErrors are supposed to describe data invalidity 102 invalidMessageErrors = append(invalidMessageErrors, 103 api.NewInvalidMessageError(derivedValue.Value, ivError, originalMessage)) 104 } else { 105 s.Log.Warn("Validation of KVDescriptor %s (derived value validation) "+ 106 "returns error that is not InvalidValueError. Using conversion "+ 107 "to InvalidValueError, but the invalid fields will be unknown.", descriptor.Name) 108 invalidMessageErrors = append(invalidMessageErrors, api.NewInvalidMessageError( 109 derivedValue.Value, api.NewInvalidValueError(err, ""), originalMessage)) 110 } 111 } 112 } 113 } 114 115 if len(invalidMessageErrors) > 0 { 116 return api.NewInvalidMessagesError(invalidMessageErrors) 117 } 118 return nil 119 }