Skip to content

Commit

Permalink
Adds Playground modes and UI changes. (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
joaovictor3g authored May 8, 2024
2 parents 9d5b7a2 + 43531d9 commit b0471a3
Show file tree
Hide file tree
Showing 105 changed files with 7,344 additions and 545 deletions.
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,23 @@ serve: ## Serve static files.
#go run cmd/server/main.go --dir web/

.PHONY: update-data
update-data: ## Update the web/assets/data.json file.
yq -ojson '.' examples.yaml > web/assets/data.json
yq -ojson -i '.versions.cel-go = "$(CEL_GO_VERSION)"' web/assets/data.json
update-data: ## Update the example files
yq -ojson '.' examples.yaml > web/assets/examples/cel.json
yq -ojson -i '.versions.cel-go = "$(CEL_GO_VERSION)"' web/assets/examples/cel.json
yq -ojson '.' validating_examples.yaml > web/assets/examples/vap.json
yq -ojson -i '.versions.cel-go = "$(CEL_GO_VERSION)"' web/assets/examples/vap.json
yq -ojson '.' webhooks_examples.yaml > web/assets/examples/webhooks.json
yq -ojson -i '.versions.cel-go = "$(CEL_GO_VERSION)"' web/assets/examples/webhooks.json

.PHONY: addlicense
addlicense: ## Add copyright license headers in source code files.
@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" .
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" -ignore "k8s/testdata/**" .

.PHONY: checklicense
checklicense: ## Check copyright license headers in source code files.
@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" -check .
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" -ignore "k8s/testdata/**" -check .

##@ Build

Expand Down
70 changes: 56 additions & 14 deletions cmd/wasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,73 @@ import (
"fmt"
"syscall/js"

"gopkg.in/yaml.v3"

"github.com/undistro/cel-playground/eval"
"github.com/undistro/cel-playground/k8s"
)

type execFunction func(mode string, argMap js.Value) (string, error)

func getArg(value js.Value, name string) []byte {
arg := value.Get(name)
if arg.Type() == js.TypeString {
return []byte(arg.String())
}
return []byte{}
}

var modeExecFns = map[string]execFunction{
"cel": func(mode string, argMap js.Value) (string, error) {
return eval.CelEval(
getArg(argMap, "cel"),
getArg(argMap, "dataInput"),
)
},
"vap": func(mode string, argMap js.Value) (string, error) {
return k8s.EvalValidatingAdmissionPolicy(
getArg(argMap, "vap"),
getArg(argMap, "dataOldObject"),
getArg(argMap, "dataObject"),
getArg(argMap, "dataNamespace"),
getArg(argMap, "dataRequest"),
getArg(argMap, "dataAuthorizer"),
)
},
"webhooks": func(mode string, argMap js.Value) (string, error) {
return k8s.EvalWebhook(
getArg(argMap, "webhooks"),
getArg(argMap, "dataOldObject"),
getArg(argMap, "dataObject"),
getArg(argMap, "dataRequest"),
getArg(argMap, "dataAuthorizer"),
)
},
}

func main() {
evalFunc := js.FuncOf(evalWrapper)
js.Global().Set("eval", evalFunc)
defer evalFunc.Release()
defer addFunction("eval", dynamicEvalWrapper).Release()
<-make(chan bool)
}

// evalWrapper wraps the eval function with `syscall/js` parameters
func evalWrapper(_ js.Value, args []js.Value) any {
func addFunction(name string, fn func(js.Value, []js.Value) any) js.Func {
function := js.FuncOf(fn)
js.Global().Set(name, function)
return function
}

func dynamicEvalWrapper(_ js.Value, args []js.Value) any {
if len(args) < 2 {
return response("", errors.New("invalid arguments"))
}
exp := args[0].String()
is := args[1].String()

var input map[string]any
if err := yaml.Unmarshal([]byte(is), &input); err != nil {
return response("", fmt.Errorf("failed to decode input: %w", err))
if args[0].Type() != js.TypeString || args[1].Type() != js.TypeObject {
return response("", errors.New("invalid argument types, expecting string and object"))
}
mode := args[0].String()
fn, ok := modeExecFns[mode]
if !ok {
return response("", fmt.Errorf("unknown mode %s", mode))
}
output, err := eval.Eval(exp, input)

output, err := fn(mode, args[1])
if err != nil {
return response("", err)
}
Expand Down
9 changes: 9 additions & 0 deletions eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/google/cel-go/ext"
"github.com/google/cel-go/interpreter"
"google.golang.org/protobuf/types/known/structpb"
"gopkg.in/yaml.v2"
k8s "k8s.io/apiserver/pkg/cel/library"
)

Expand Down Expand Up @@ -76,6 +77,14 @@ var celProgramOptions = []cel.ProgramOption{
cel.CostTrackerOptions(interpreter.PresenceTestHasCost(false)),
}

func CelEval(exp []byte, input []byte) (string, error) {
var inputMap map[string]any
if err := yaml.Unmarshal(input, &inputMap); err != nil {
return "", fmt.Errorf("failed to decode input: %w", err)
}
return Eval(string(exp), inputMap)
}

// Eval evaluates the cel expression against the given input
func Eval(exp string, input map[string]any) (string, error) {
inputVars := make([]cel.EnvOption, 0, len(input))
Expand Down
28 changes: 14 additions & 14 deletions examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ examples:
|| (account.overdraftProtection
&& account.overdraftLimit >= transaction.withdrawal - account.balance)
data: |
dataInput: |
# Here is the input data in YAML or JSON format.
account:
Expand All @@ -46,7 +46,7 @@ examples:
container.image.startsWith(registry)
)
)
data: |
dataInput: |
params:
allowedRegistries:
- myregistry.com
Expand Down Expand Up @@ -83,7 +83,7 @@ examples:
port.hostPort == 0
)
)
data: |
dataInput: |
object:
apiVersion: apps/v1
kind: Deployment
Expand Down Expand Up @@ -129,7 +129,7 @@ examples:
!has(container.securityContext) || !has(container.securityContext.runAsNonRoot) || container.securityContext.runAsNonRoot != false
)
)
data: |
dataInput: |
object:
apiVersion: apps/v1
kind: Deployment
Expand Down Expand Up @@ -175,7 +175,7 @@ examples:
!has(container.securityContext.capabilities.add) ||
container.securityContext.capabilities.add.all(cap, cap in params.allowedCapabilities)
)
data: |
dataInput: |
params:
allowedCapabilities: [NET_BIND_SERVICE]
object:
Expand Down Expand Up @@ -214,7 +214,7 @@ examples:
// the regex above is suggested by semver.org: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
// allowing the "v" prefix
)
data: |
dataInput: |
object:
apiVersion: v1
kind: Pod
Expand Down Expand Up @@ -244,7 +244,7 @@ examples:
&& url(object.href).getPort() == '80'
&& url(object.href).getEscapedPath() == '/path'
&& url(object.href).getQuery().size() == 1
data: |
dataInput: |
{
"object": {
"href": "https://user:[email protected]:80/path?query=val#fragment"
Expand All @@ -266,7 +266,7 @@ examples:
.filter(c, c.startsWith('group'))
.all(c, jwt.extra_claims[c]
.all(g, g.endsWith('@acme.co')))
data: |
dataInput: |
jwt: {
"iss": "auth.acme.com:12350",
"sub": "serviceAccount:[email protected]",
Expand All @@ -286,15 +286,15 @@ examples:

- name: "Optional"
cel: 'object.?foo.orValue("fallback")'
data: "object: {}"
dataInput: "object: {}"
category: "General"

- name: "Duration and timestamp"
cel: |
// Validate that 'expired' date is after a 'created' date plus a 'ttl' duration
has(object.expired) &&
timestamp(object.created) + duration(object.ttl) < timestamp(object.expired)
data: |
dataInput: |
object:
created: "2023-06-14T02:00:14+00:00"
ttl: "5m"
Expand All @@ -310,7 +310,7 @@ examples:
.add(quantity("700M"))
.sub(1) // test without this subtraction
.isLessThan(quantity(object.limit))
data: |
dataInput: |
object:
memory: 1.3G
limit: 2G
Expand All @@ -334,7 +334,7 @@ examples:
// expression: "response.code >= 400 || xds.cluster_name == 'BlackHoleCluster' || xds.cluster_name == 'PassthroughCluster' "
response.code >= 400 || (xds.cluster_name == 'BlackHoleCluster' || xds.cluster_name == 'PassthroughCluster')
data: |
dataInput: |
# The following configuration is true access logs only when the response code is greater or equal to 400
# or the request went to the BlackHoleCluster or the PassthroughCluster
request:
Expand Down Expand Up @@ -404,7 +404,7 @@ examples:
// value: "request.host" # <--- CEL
has(request.host) ? request.host : "unknown"
data: |
dataInput: |
request:
duration: "4.144461ms"
headers:
Expand Down Expand Up @@ -474,5 +474,5 @@ examples:

- name: "Blank"
cel: ""
data: ""
dataInput: ""
category: "Blank"
34 changes: 16 additions & 18 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
module github.com/undistro/cel-playground

go 1.21
go 1.22.0

require (
github.com/google/cel-go v0.17.8
google.golang.org/protobuf v1.31.0
google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apiserver v0.28.4
k8s.io/api v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/apiserver v0.30.0
k8s.io/client-go v0.30.0
)

require (
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
Expand All @@ -29,27 +33,21 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.28.4 // indirect
k8s.io/apimachinery v0.28.4 // indirect
k8s.io/client-go v0.28.4 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace k8s.io/apiserver v0.28.4 => github.com/kubernetes/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20231128103858-022d50fe3a1b
Loading

0 comments on commit b0471a3

Please sign in to comment.