@@ -21,6 +21,7 @@ import (
2121 "fmt"
2222 "reflect"
2323
24+ "github.com/blang/semver/v4"
2425 "github.com/google/cel-go/cel"
2526 "github.com/google/cel-go/common/types"
2627 "github.com/google/cel-go/common/types/traits"
@@ -105,43 +106,54 @@ var valueTypes = map[string]struct {
105106 celType * cel.Type
106107 // get returns nil if the attribute doesn't have the type, otherwise
107108 // the value of that type.
108- get func (attr resourceapi.NamedResourcesAttribute ) any
109+ get func (attr resourceapi.NamedResourcesAttribute ) ( any , error )
109110}{
110- "quantity" : {apiservercel .QuantityType , func (attr resourceapi.NamedResourcesAttribute ) any {
111+ "quantity" : {apiservercel .QuantityType , func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
111112 if attr .QuantityValue == nil {
112- return nil
113+ return nil , nil
113114 }
114- return apiservercel.Quantity {Quantity : attr .QuantityValue }
115+ return apiservercel.Quantity {Quantity : attr .QuantityValue }, nil
115116 }},
116- "bool" : {cel .BoolType , func (attr resourceapi.NamedResourcesAttribute ) any {
117+ "bool" : {cel .BoolType , func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
117118 if attr .BoolValue == nil {
118- return nil
119+ return nil , nil
119120 }
120- return * attr .BoolValue
121+ return * attr .BoolValue , nil
121122 }},
122- "int" : {cel .IntType , func (attr resourceapi.NamedResourcesAttribute ) any {
123+ "int" : {cel .IntType , func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
123124 if attr .IntValue == nil {
124- return nil
125+ return nil , nil
125126 }
126- return * attr .IntValue
127+ return * attr .IntValue , nil
127128 }},
128- "intslice" : {types .NewListType (cel .IntType ), func (attr resourceapi.NamedResourcesAttribute ) any {
129+ "intslice" : {types .NewListType (cel .IntType ), func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
129130 if attr .IntSliceValue == nil {
130- return nil
131+ return nil , nil
131132 }
132- return attr .IntSliceValue .Ints
133+ return attr .IntSliceValue .Ints , nil
133134 }},
134- "string" : {cel .StringType , func (attr resourceapi.NamedResourcesAttribute ) any {
135+ "string" : {cel .StringType , func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
135136 if attr .StringValue == nil {
136- return nil
137+ return nil , nil
137138 }
138- return * attr .StringValue
139+ return * attr .StringValue , nil
139140 }},
140- "stringslice" : {types .NewListType (cel .StringType ), func (attr resourceapi.NamedResourcesAttribute ) any {
141+ "stringslice" : {types .NewListType (cel .StringType ), func (attr resourceapi.NamedResourcesAttribute ) ( any , error ) {
141142 if attr .StringSliceValue == nil {
142- return nil
143+ return nil , nil
143144 }
144- return attr .StringSliceValue .Strings
145+ return attr .StringSliceValue .Strings , nil
146+ }},
147+ "version" : {SemverType , func (attr resourceapi.NamedResourcesAttribute ) (any , error ) {
148+ if attr .VersionValue == nil {
149+ return nil , nil
150+ }
151+ v , err := semver .Parse (* attr .VersionValue )
152+ if err != nil {
153+ return nil , fmt .Errorf ("parse semantic version: %v" , err )
154+ }
155+
156+ return Semver {Version : v }, nil
145157 }},
146158}
147159
@@ -150,7 +162,11 @@ var boolType = reflect.TypeOf(true)
150162func (c CompilationResult ) Evaluate (ctx context.Context , attributes []resourceapi.NamedResourcesAttribute ) (bool , error ) {
151163 variables := make (map [string ]any , len (valueTypes ))
152164 for name , valueType := range valueTypes {
153- variables [attributesVarPrefix + name ] = buildValueMapper (c .Environment .CELTypeAdapter (), attributes , valueType .get )
165+ m , err := buildValueMapper (c .Environment .CELTypeAdapter (), attributes , valueType .get )
166+ if err != nil {
167+ return false , fmt .Errorf ("extract attributes with type %s: %v" , name , err )
168+ }
169+ variables [attributesVarPrefix + name ] = m
154170 }
155171 result , _ , err := c .Program .ContextEval (ctx , variables )
156172 if err != nil {
@@ -172,7 +188,9 @@ func mustBuildEnv() *environment.EnvSet {
172188 versioned := []environment.VersionedOptions {
173189 {
174190 IntroducedVersion : version .MajorMinor (1 , 30 ),
175- EnvOptions : buildVersionedAttributes (),
191+ EnvOptions : append (buildVersionedAttributes (),
192+ SemverLib (),
193+ ),
176194 },
177195 }
178196 envset , err := envset .Extend (versioned ... )
@@ -190,17 +208,21 @@ func buildVersionedAttributes() []cel.EnvOption {
190208 return options
191209}
192210
193- func buildValueMapper (adapter types.Adapter , attributes []resourceapi.NamedResourcesAttribute , get func (resourceapi.NamedResourcesAttribute ) any ) traits.Mapper {
211+ func buildValueMapper (adapter types.Adapter , attributes []resourceapi.NamedResourcesAttribute , get func (resourceapi.NamedResourcesAttribute ) ( any , error )) ( traits.Mapper , error ) {
194212 // This implementation constructs a map and then let's cel handle the
195213 // lookup and iteration. This is done for the sake of simplicity.
196214 // Whether it's faster than writing a custom mapper depends on
197215 // real-world attribute sets and CEL expressions and would have to be
198216 // benchmarked.
199217 valueMap := make (map [string ]any )
200- for _ , attribute := range attributes {
201- if value := get (attribute ); value != nil {
218+ for name , attribute := range attributes {
219+ value , err := get (attribute )
220+ if err != nil {
221+ return nil , fmt .Errorf ("attribute %q: %v" , name , err )
222+ }
223+ if value != nil {
202224 valueMap [attribute .Name ] = value
203225 }
204226 }
205- return types .NewStringInterfaceMap (adapter , valueMap )
227+ return types .NewStringInterfaceMap (adapter , valueMap ), nil
206228}
0 commit comments