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

Commit 68a198d

Browse files
TzlilSwimmer123teselil
and
teselil
authored
feat: add custom key for cel expression support (#961)
* feat: add custom key for cel expression support --------- Co-authored-by: teselil <[email protected]>
1 parent 5d637d2 commit 68a198d

11 files changed

+337
-22
lines changed

examples/CEL/policy.yaml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
apiVersion: v1
2+
policies:
3+
- name: CEL_policy
4+
isDefault: true
5+
rules:
6+
- identifier: CUSTOM_DEPLOYMENT_BILLING_LABEL_EXISTS
7+
messageOnFailure: "workloads labels should contain billing label"
8+
- identifier: CUSTOM_SECRET_ENVIRONMENT_LABEL_EXISTS
9+
messageOnFailure: "secret labels should contain environment label"
10+
11+
12+
customRules:
13+
- identifier: CUSTOM_WORKLOADS_BILLING_LABEL_EXISTS
14+
name: Ensure Workloads has billing label [CUSTOM RULE]
15+
defaultMessageOnFailure: workloads labels should contain billing label
16+
schema:
17+
# constraint schema
18+
if:
19+
properties:
20+
kind:
21+
type: string
22+
enum:
23+
- Deployment
24+
- Pod
25+
then:
26+
CELDefinition:
27+
- expression: "object.kind != 'Deployment' || (has(object.metadata.labels) && has(object.metadata.labels.billing))"
28+
message: "deployment labels should contain billing label"
29+
- expression: "object.kind != 'Pod' || (has(object.metadata.labels) && has(object.metadata.labels.billing))"
30+
message: "pod labels should contain billing label"
31+
- identifier: CUSTOM_SECRET_ENVIRONMENT_LABEL_EXISTS
32+
name: Ensure Secret has environment label [CUSTOM RULE]
33+
defaultMessageOnFailure: secret labels should contain environment label
34+
schema:
35+
# constraint schema
36+
if:
37+
properties:
38+
kind:
39+
type: string
40+
enum:
41+
- Secret
42+
then:
43+
CELDefinition:
44+
- expression: "has(object.metadata.labels) && has(object.metadata.labels.environment)"
45+

go.mod

+13-4
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ require (
2929
sigs.k8s.io/yaml v1.3.0
3030
)
3131

32-
require github.com/open-policy-agent/opa v0.49.2
32+
require (
33+
github.com/google/cel-go v0.16.0
34+
github.com/open-policy-agent/opa v0.49.2
35+
)
3336

3437
require (
3538
github.com/OneOfOne/xxhash v1.2.8 // indirect
3639
github.com/a8m/envsubst v1.3.0 // indirect
3740
github.com/agnivade/levenshtein v1.1.1 // indirect
3841
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
42+
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
3943
github.com/elliotchance/orderedmap v1.4.0 // indirect
4044
github.com/gobwas/glob v0.2.3 // indirect
4145
github.com/goccy/go-json v0.9.11 // indirect
@@ -46,10 +50,15 @@ require (
4650
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
4751
github.com/rivo/uniseg v0.2.0 // indirect
4852
github.com/rogpeppe/go-internal v1.10.0 // indirect
53+
github.com/stoewer/go-strcase v1.2.0 // indirect
4954
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
5055
github.com/yashtewari/glob-intersection v0.1.0 // indirect
51-
golang.org/x/net v0.8.0 // indirect
56+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
57+
golang.org/x/net v0.10.0 // indirect
5258
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
59+
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
60+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
61+
google.golang.org/protobuf v1.31.0 // indirect
5362
gopkg.in/inf.v0 v0.9.1 // indirect
5463
)
5564

@@ -78,8 +87,8 @@ require (
7887
github.com/tklauser/numcpus v0.3.0 // indirect
7988
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
8089
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
81-
golang.org/x/sys v0.6.0 // indirect
82-
golang.org/x/text v0.8.0 // indirect
90+
golang.org/x/sys v0.8.0 // indirect
91+
golang.org/x/text v0.9.0 // indirect
8392
gopkg.in/ini.v1 v1.51.0 // indirect
8493
)
8594

go.sum

+23-7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0q
5353
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
5454
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
5555
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
56+
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
57+
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
5658
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
5759
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
5860
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
@@ -168,10 +170,13 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
168170
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
169171
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
170172
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
173+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
171174
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
172175
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
173176
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
174177
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
178+
github.com/google/cel-go v0.16.0 h1:DG9YQ8nFCFXAs/FDDwBxmL1tpKNrdlGUM9U3537bX/Y=
179+
github.com/google/cel-go v0.16.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
175180
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
176181
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
177182
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -182,6 +187,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
182187
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
183188
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
184189
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
190+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
185191
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
186192
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
187193
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -380,6 +386,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
380386
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
381387
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
382388
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
389+
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
390+
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
383391
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
384392
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
385393
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -454,6 +462,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
454462
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
455463
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
456464
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
465+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
466+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
457467
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
458468
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
459469
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -512,8 +522,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
512522
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
513523
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
514524
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
515-
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
516-
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
525+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
526+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
517527
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
518528
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
519529
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -583,8 +593,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
583593
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
584594
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
585595
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
586-
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
587-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
596+
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
597+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
588598
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
589599
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
590600
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -593,8 +603,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
593603
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
594604
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
595605
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
596-
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
597-
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
606+
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
607+
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
598608
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
599609
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
600610
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -718,6 +728,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
718728
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
719729
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
720730
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
731+
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
732+
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
733+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc=
734+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
721735
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
722736
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
723737
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -744,7 +758,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
744758
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
745759
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
746760
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
747-
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
761+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
762+
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
763+
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
748764
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
749765
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
750766
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// This file defines a custom key to implement the logic for cel rule:
2+
3+
package jsonSchemaValidator
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
9+
"github.com/google/cel-go/cel"
10+
"github.com/santhosh-tekuri/jsonschema/v5"
11+
)
12+
13+
const CELDefinitionCustomKey = "CELDefinition"
14+
15+
type CustomKeyCELDefinitionCompiler struct{}
16+
17+
type CustomKeyCELDefinitionSchema []interface{}
18+
19+
var CustomKeyCELRule = jsonschema.MustCompileString("customKeyCELDefinition.json", `{
20+
"properties" : {
21+
"CELDefinition": {
22+
"type": "array"
23+
}
24+
}
25+
}`)
26+
27+
func (CustomKeyCELDefinitionCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) {
28+
if customKeyCELRule, ok := m[CELDefinitionCustomKey]; ok {
29+
customKeyCELRuleObj, validObject := customKeyCELRule.([]interface{})
30+
if !validObject {
31+
return nil, fmt.Errorf("CELDefinition must be an array")
32+
}
33+
34+
CELDefinitionSchema, err := convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(customKeyCELRuleObj)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
if len(CELDefinitionSchema.CELExpressions) == 0 {
40+
return nil, fmt.Errorf("CELDefinition can't be empty")
41+
}
42+
43+
return CustomKeyCELDefinitionSchema(customKeyCELRuleObj), nil
44+
}
45+
return nil, nil
46+
}
47+
48+
func (customKeyCELDefinitionSchema CustomKeyCELDefinitionSchema) Validate(ctx jsonschema.ValidationContext, dataValue interface{}) error {
49+
CELDefinitionSchema, err := convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(customKeyCELDefinitionSchema)
50+
if err != nil {
51+
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
52+
}
53+
// wrap dataValue (the resource that should be validated) inside a struct with parent object key
54+
resourceWithParentKey := make(map[string]interface{})
55+
resourceWithParentKey["object"] = dataValue
56+
57+
// prepare CEL env inputs - in our case the only input is the resource that should be validated
58+
inputs, err := getCELEnvInputs(resourceWithParentKey)
59+
if err != nil {
60+
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
61+
}
62+
63+
env, err := cel.NewEnv(inputs...)
64+
if err != nil {
65+
return ctx.Error(CustomKeyValidationErrorKeyPath, err.Error())
66+
}
67+
68+
for _, celExpression := range CELDefinitionSchema.CELExpressions {
69+
ast, issues := env.Compile(celExpression.Expression)
70+
if issues != nil && issues.Err() != nil {
71+
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression compile error: %s", issues.Err())
72+
}
73+
74+
prg, err := env.Program(ast)
75+
if err != nil {
76+
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel program construction error: %s", err)
77+
}
78+
79+
res1, _, err := prg.Eval(resourceWithParentKey)
80+
if err != nil {
81+
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel evaluation error: %s", err)
82+
}
83+
84+
if res1.Type().TypeName() != "bool" {
85+
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression needs to return a boolean")
86+
}
87+
88+
celReturnValue, ok := res1.Value().(bool)
89+
if !ok {
90+
return ctx.Error(CustomKeyValidationErrorKeyPath, "cel expression needs to return a boolean")
91+
}
92+
if !celReturnValue {
93+
return ctx.Error(CELDefinitionCustomKey, "values in data value %v do not match", dataValue)
94+
}
95+
}
96+
97+
return nil
98+
}
99+
100+
type CELExpression struct {
101+
Expression string `json:"expression"`
102+
}
103+
104+
type CELDefinition struct {
105+
CELExpressions []CELExpression
106+
}
107+
108+
func convertCustomKeyCELDefinitionSchemaToCELDefinitionSchema(CELDefinitionSchema CustomKeyCELDefinitionSchema) (*CELDefinition, error) {
109+
var CELDefinition CELDefinition
110+
for _, CELExpressionFromSchema := range CELDefinitionSchema {
111+
var CELExpression CELExpression
112+
b, err := json.Marshal(CELExpressionFromSchema)
113+
if err != nil {
114+
return nil, fmt.Errorf("CELExpression failed to marshal to json, %s", err.Error())
115+
}
116+
err = json.Unmarshal(b, &CELExpression)
117+
if err != nil {
118+
return nil, fmt.Errorf("CELExpression must be an object of type CELExpression %s", err.Error())
119+
}
120+
CELDefinition.CELExpressions = append(CELDefinition.CELExpressions, CELExpression)
121+
}
122+
123+
return &CELDefinition, nil
124+
}
125+
126+
func getCELEnvInputs(dataValue map[string]interface{}) ([]cel.EnvOption, error) {
127+
inputVars := make([]cel.EnvOption, 0, len(dataValue))
128+
for input := range dataValue {
129+
inputVars = append(inputVars, cel.Variable(input, cel.DynType))
130+
}
131+
return inputVars, nil
132+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"CELDefinition": [
3+
{
4+
"expression": "hassss(object.metadata.labels) && has(object.metadata.labels.billing)",
5+
"message": "deployment labels should contain billing label"
6+
}
7+
]
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"CELDefinition": [
3+
{
4+
"expression": 1,
5+
"message": "deployment labels should contain billing label"
6+
}
7+
]
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"CELDefinition": [
3+
{
4+
"expression": "has(object.metadata.labels) && has(object.metadata.labels.billing)",
5+
"message": "deployment labels should contain billing label"
6+
}
7+
]
8+
}

pkg/jsonSchemaValidator/validator.go

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func (jsv *JSONSchemaValidator) Validate(schemaContent string, yamlContent []byt
7373
compiler.RegisterExtension("customKeyRule89", extensions.CustomKeyRule89, extensions.CustomKeyRule89Compiler{})
7474
compiler.RegisterExtension("customKeyRule101", extensions.CustomKeyRule101, extensions.CustomKeyRule101Compiler{})
7575
compiler.RegisterExtension("customKeyRegoRule", extensions.CustomKeyRegoRule, extensions.CustomKeyRegoDefinitionCompiler{})
76+
compiler.RegisterExtension("customKeyCELRule", extensions.CustomKeyCELRule, extensions.CustomKeyCELDefinitionCompiler{})
7677

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

0 commit comments

Comments
 (0)