-
Notifications
You must be signed in to change notification settings - Fork 124
/
Copy pathvalidation.go
121 lines (111 loc) · 5.31 KB
/
validation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright (c) 2020 Pantheon.tech
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvscheduler
import (
"github.com/go-errors/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/dynamicpb"
"go.ligato.io/vpp-agent/v3/pkg/models"
"go.ligato.io/vpp-agent/v3/pkg/util"
"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
)
// ValidateSemantically validates given proto messages according to semantic validation (KVDescriptor.Validate)
// from registered KVDescriptors. If all messages are valid, nil is returned. If some messages are invalid,
// kvscheduler.MessageValidationErrors is returned. In any other case, error is returned.
//
// Usage of dynamic proto messages (dynamicpb.Message) is supported only when the corresponding model contains
// statically generated proto message (in this case the dynamic message can be converted to static).
// The reason for this is that the KVDescriptors can validate only statically generated proto messages and
// dynamic proto messages can't generally be converted to such proto messages (if corresponding statically generated
// proto messages are not available).
func (s *Scheduler) ValidateSemantically(messages []proto.Message) error {
s.txnLock.Lock()
defer s.txnLock.Unlock()
invalidMessageErrors := make([]*api.InvalidMessageError, 0)
for _, message := range messages {
originalMessage := message
// if needed, convert dynamic proto message to statically generated proto message
// (validators in descriptors can validate only statically generated proto messages)
if dynamicMessage, isDyn := message.(*dynamicpb.Message); isDyn {
model, err := models.GetModelFor(message)
if err != nil {
return errors.Errorf("can't get model for message due to: %v (message=%v)", err, message)
}
goType := model.LocalGoType() // only static known models will return meaningful go type
if goType == nil {
s.Log.Debug("instance of %s doesn't have local go type and will be skipped from validation "+
"using KVDescriptor.Validate (dynamic message=%v)", model.ProtoName(), message)
continue
}
message, err = util.ConvertProto(model.NewInstance(), dynamicMessage)
if err != nil {
return errors.Errorf("can't convert dynamic message to model proto message "+
"due to: %v (dynamic message=%v)", err, dynamicMessage)
}
}
// get descriptor and key for given message
key, err := models.GetKey(message)
if err != nil {
return errors.Errorf("can't get message key due to: %v (message=%v)", err, message)
}
descriptor := s.registry.GetDescriptorForKey(key)
if descriptor == nil {
s.Log.Debug("Skipping validation for proto message key %s "+
"due to missing descriptor (proto message: %v)", key, message)
continue
}
descHandler := newDescriptorHandler(descriptor)
// validate and collect validation errors
if err = descHandler.validate(key, message); err != nil {
if ivError, ok := err.(*api.InvalidValueError); ok {
// only InvalidValueErrors are supposed to describe data invalidity
invalidMessageErrors = append(invalidMessageErrors,
api.NewInvalidMessageError(originalMessage, ivError, nil))
} else {
s.Log.Warn("Validation of KVDescriptor %s returns error that is not InvalidValueError. "+
"Using conversion to InvalidValueError, but the invalid fields will be unknown.", descriptor.Name)
invalidMessageErrors = append(invalidMessageErrors, api.NewInvalidMessageError(
originalMessage, api.NewInvalidValueError(err, ""), nil))
}
}
// validate also derived values
for _, derivedValue := range descHandler.derivedValues(key, message) {
descriptor = s.registry.GetDescriptorForKey(derivedValue.Key)
if descriptor == nil {
s.Log.Debug("Skipping validation for proto message's derived value key %s "+
"due to missing descriptor (proto message: %v, derived value proto message: %v)",
derivedValue.Key, message, derivedValue.Value)
continue
}
descHandler = newDescriptorHandler(descriptor)
if err = descHandler.validate(derivedValue.Key, derivedValue.Value); err != nil {
if ivError, ok := err.(*api.InvalidValueError); ok {
// only InvalidValueErrors are supposed to describe data invalidity
invalidMessageErrors = append(invalidMessageErrors,
api.NewInvalidMessageError(derivedValue.Value, ivError, originalMessage))
} else {
s.Log.Warn("Validation of KVDescriptor %s (derived value validation) "+
"returns error that is not InvalidValueError. Using conversion "+
"to InvalidValueError, but the invalid fields will be unknown.", descriptor.Name)
invalidMessageErrors = append(invalidMessageErrors, api.NewInvalidMessageError(
derivedValue.Value, api.NewInvalidValueError(err, ""), originalMessage))
}
}
}
}
if len(invalidMessageErrors) > 0 {
return api.NewInvalidMessagesError(invalidMessageErrors)
}
return nil
}