Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

Commit

Permalink
feat: add custom key for cel expression support (#961)
Browse files Browse the repository at this point in the history
* feat: add custom key for cel expression support

---------

Co-authored-by: teselil <tzlil@datree.com>
  • Loading branch information
TzlilSwimmer123 and teselil authored Jul 20, 2023
1 parent 5d637d2 commit 68a198d
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 22 deletions.
45 changes: 45 additions & 0 deletions examples/CEL/policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
apiVersion: v1
policies:
- name: CEL_policy
isDefault: true
rules:
- identifier: CUSTOM_DEPLOYMENT_BILLING_LABEL_EXISTS
messageOnFailure: "workloads labels should contain billing label"
- identifier: CUSTOM_SECRET_ENVIRONMENT_LABEL_EXISTS
messageOnFailure: "secret labels should contain environment label"


customRules:
- identifier: CUSTOM_WORKLOADS_BILLING_LABEL_EXISTS
name: Ensure Workloads has billing label [CUSTOM RULE]
defaultMessageOnFailure: workloads labels should contain billing label
schema:
# constraint schema
if:
properties:
kind:
type: string
enum:
- Deployment
- Pod
then:
CELDefinition:
- expression: "object.kind != 'Deployment' || (has(object.metadata.labels) && has(object.metadata.labels.billing))"
message: "deployment labels should contain billing label"
- expression: "object.kind != 'Pod' || (has(object.metadata.labels) && has(object.metadata.labels.billing))"
message: "pod labels should contain billing label"
- identifier: CUSTOM_SECRET_ENVIRONMENT_LABEL_EXISTS
name: Ensure Secret has environment label [CUSTOM RULE]
defaultMessageOnFailure: secret labels should contain environment label
schema:
# constraint schema
if:
properties:
kind:
type: string
enum:
- Secret
then:
CELDefinition:
- expression: "has(object.metadata.labels) && has(object.metadata.labels.environment)"

17 changes: 13 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ require (
sigs.k8s.io/yaml v1.3.0
)

require github.com/open-policy-agent/opa v0.49.2
require (
github.com/google/cel-go v0.16.0
github.com/open-policy-agent/opa v0.49.2
)

require (
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/a8m/envsubst v1.3.0 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/elliotchance/orderedmap v1.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.9.11 // indirect
Expand All @@ -46,10 +50,15 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
)

Expand Down Expand Up @@ -78,8 +87,8 @@ require (
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect
)

Expand Down
30 changes: 23 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0q
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
Expand Down Expand Up @@ -168,10 +170,13 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.16.0 h1:DG9YQ8nFCFXAs/FDDwBxmL1tpKNrdlGUM9U3537bX/Y=
github.com/google/cel-go v0.16.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand All @@ -182,6 +187,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
Expand Down Expand Up @@ -380,6 +386,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
Expand Down Expand Up @@ -454,6 +462,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -512,8 +522,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -583,8 +593,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -593,8 +603,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -718,6 +728,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
Expand All @@ -744,7 +758,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
132 changes: 132 additions & 0 deletions pkg/jsonSchemaValidator/extensions/customKeyCELDefinition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// This file defines a custom key to implement the logic for cel rule:

package jsonSchemaValidator

import (
"encoding/json"
"fmt"

"github.com/google/cel-go/cel"
"github.com/santhosh-tekuri/jsonschema/v5"
)

const CELDefinitionCustomKey = "CELDefinition"

type CustomKeyCELDefinitionCompiler struct{}

type CustomKeyCELDefinitionSchema []interface{}

var CustomKeyCELRule = jsonschema.MustCompileString("customKeyCELDefinition.json", `{
"properties" : {
"CELDefinition": {
"type": "array"
}
}
}`)

func (CustomKeyCELDefinitionCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) {
if customKeyCELRule, ok := m[CELDefinitionCustomKey]; ok {
customKeyCELRuleObj, validObject := customKeyCELRule.([]interface{})
if !validObject {
return nil, fmt.Errorf("CELDefinition must be an array")
}

CELDefinitionSchema, err := convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(customKeyCELRuleObj)
if err != nil {
return nil, err
}

if len(CELDefinitionSchema.CELExpressions) == 0 {
return nil, fmt.Errorf("CELDefinition can't be empty")
}

return CustomKeyCELDefinitionSchema(customKeyCELRuleObj), nil
}
return nil, nil
}

func (customKeyCELDefinitionSchema CustomKeyCELDefinitionSchema) Validate(ctx jsonschema.ValidationContext, dataValue interface{}) error {
CELDefinitionSchema, err := convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(customKeyCELDefinitionSchema)
if err != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
}
// wrap dataValue (the resource that should be validated) inside a struct with parent object key
resourceWithParentKey := make(map[string]interface{})
resourceWithParentKey["object"] = dataValue

// prepare CEL env inputs - in our case the only input is the resource that should be validated
inputs, err := getCELEnvInputs(resourceWithParentKey)
if err != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
}

env, err := cel.NewEnv(inputs...)
if err != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
}

for _, celExpression := range CELDefinitionSchema.CELExpressions {
ast, issues := env.Compile(celExpression.Expression)
if issues != nil && issues.Err() != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression compile error: %s", issues.Err())
}

prg, err := env.Program(ast)
if err != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel program construction error: %s", err)
}

res1, _, err := prg.Eval(resourceWithParentKey)
if err != nil {
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel evaluation error: %s", err)
}

if res1.Type().TypeName() != "bool" {
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression needs to return a boolean")
}

celReturnValue, ok := res1.Value().(bool)
if !ok {
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression needs to return a boolean")
}
if !celReturnValue {
return ctx.Error(CELDefinitionCustomKey, "values in data value %v do not match", dataValue)
}
}

return nil
}

type CELExpression struct {
Expression string `json:"expression"`
}

type CELDefinition struct {
CELExpressions []CELExpression
}

func convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(CELDefinitionSchema CustomKeyCELDefinitionSchema) (*CELDefinition, error) {
var CELDefinition CELDefinition
for _, CELExpressionFromSchema := range CELDefinitionSchema {
var CELExpression CELExpression
b, err := json.Marshal(CELExpressionFromSchema)
if err != nil {
return nil, fmt.Errorf("CELExpression failed to marshal to json, %s", err.Error())
}
err = json.Unmarshal(b, &CELExpression)
if err != nil {
return nil, fmt.Errorf("CELExpression must be an object of type CELExpression %s", err.Error())
}
CELDefinition.CELExpressions = append(CELDefinition.CELExpressions, CELExpression)
}

return &CELDefinition, nil
}

func getCELEnvInputs(dataValue map[string]interface{}) ([]cel.EnvOption, error) {
inputVars := make([]cel.EnvOption, 0, len(dataValue))
for input := range dataValue {
inputVars = append(inputVars, cel.Variable(input, cel.DynType))
}
return inputVars, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"CELDefinition": [
{
"expression": "hassss(object.metadata.labels) && has(object.metadata.labels.billing)",
"message": "deployment labels should contain billing label"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"CELDefinition": [
{
"expression": 1,
"message": "deployment labels should contain billing label"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"CELDefinition": [
{
"expression": "has(object.metadata.labels) && has(object.metadata.labels.billing)",
"message": "deployment labels should contain billing label"
}
]
}
1 change: 1 addition & 0 deletions pkg/jsonSchemaValidator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func (jsv *JSONSchemaValidator) Validate(schemaContent string, yamlContent []byt
compiler.RegisterExtension("customKeyRule89", extensions.CustomKeyRule89, extensions.CustomKeyRule89Compiler{})
compiler.RegisterExtension("customKeyRule101", extensions.CustomKeyRule101, extensions.CustomKeyRule101Compiler{})
compiler.RegisterExtension("customKeyRegoRule", extensions.CustomKeyRegoRule, extensions.CustomKeyRegoDefinitionCompiler{})
compiler.RegisterExtension("customKeyCELRule", extensions.CustomKeyCELRule, extensions.CustomKeyCELDefinitionCompiler{})

// compiler.Compile() is an expensive operation. We cache the compiled schema in rulesSchemasCache to avoid re-compiling the same schema.
schemaAny, ok := jsv.rulesSchemasCache.Load(schemaContent)
Expand Down
Loading

0 comments on commit 68a198d

Please sign in to comment.