From 1eda4789d1c666a5b8eac7b0525aa1b847f6d155 Mon Sep 17 00:00:00 2001
From: Khaled Emara <khaled.emara@nirmata.com>
Date: Tue, 6 Feb 2024 19:03:32 +0200
Subject: [PATCH] test(globalcontext): add e2e tests (#9661)

* fix(globalcontext): validation

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(globalcontext): use existence instead of ready for now

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* chore(globalcontext): improve not ready error message

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(globalcontext): allow any APICall

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(globalcontext): prevent double marshal

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* test(globalcontext): add e2e tests

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* chore(globalcontext): move vaildation to OpenAPI V3

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

---------

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
---
 api/kyverno/v1/common_types.go                |  1 +
 .../v2alpha1/global_context_entry_types.go    |  3 --
 pkg/controllers/webhook/controller.go         |  4 +--
 pkg/engine/context/loaders/globalcontext.go   | 11 +++++--
 pkg/globalcontext/externalapi/entry.go        |  1 -
 pkg/validation/policy/validate.go             | 32 +++++++++++++++---
 .../globalcontext/apicall-correct/README.md   | 11 +++++++
 .../apicall-correct/chainsaw-test.yaml        | 23 +++++++++++++
 .../clusterpolicy-succeeded.yaml              |  9 +++++
 .../apicall-correct/clusterpolicy.yaml        | 33 +++++++++++++++++++
 .../apicall-correct/gctxentry.yaml            |  8 +++++
 .../apicall-correct/main-deployment.yaml      | 22 +++++++++++++
 .../apicall-correct/namespace.yaml            |  4 +++
 .../new-deployment-exists.yaml                |  7 ++++
 .../apicall-correct/new-deployment.yaml       | 22 +++++++++++++
 .../globalcontext/apicall-not-exist/README.md | 11 +++++++
 .../apicall-not-exist/chainsaw-test.yaml      | 26 +++++++++++++++
 .../clusterpolicy-failed.yaml                 |  9 +++++
 .../apicall-not-exist/clusterpolicy.yaml      | 33 +++++++++++++++++++
 .../apicall-not-exist/gctxentry.yaml          |  8 +++++
 .../apicall-not-exist/main-deployment.yaml    | 22 +++++++++++++
 .../apicall-not-exist/namespace.yaml          |  4 +++
 .../apicall-not-exist/new-deployment.yaml     | 22 +++++++++++++
 .../globalcontext/resource-correct/README.md  | 11 +++++++
 .../resource-correct/chainsaw-test.yaml       | 23 +++++++++++++
 .../clusterpolicy-succeeded.yaml              |  9 +++++
 .../resource-correct/clusterpolicy.yaml       | 33 +++++++++++++++++++
 .../resource-correct/gctxentry.yaml           | 10 ++++++
 .../resource-correct/main-deployment.yaml     | 22 +++++++++++++
 .../resource-correct/namespace.yaml           |  4 +++
 .../new-deployment-exists.yaml                |  7 ++++
 .../resource-correct/new-deployment.yaml      | 22 +++++++++++++
 .../resource-not-exist/README.md              | 11 +++++++
 .../resource-not-exist/chainsaw-test.yaml     | 26 +++++++++++++++
 .../clusterpolicy-failed.yaml                 |  9 +++++
 .../resource-not-exist/clusterpolicy.yaml     | 33 +++++++++++++++++++
 .../resource-not-exist/gctxentry.yaml         | 10 ++++++
 .../resource-not-exist/main-deployment.yaml   | 22 +++++++++++++
 .../resource-not-exist/namespace.yaml         |  4 +++
 .../resource-not-exist/new-deployment.yaml    | 22 +++++++++++++
 40 files changed, 590 insertions(+), 14 deletions(-)
 create mode 100644 test/conformance/chainsaw/globalcontext/apicall-correct/README.md
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/chainsaw-test.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy-succeeded.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/gctxentry.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/main-deployment.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/namespace.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment-exists.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment.yaml
 create mode 100644 test/conformance/chainsaw/globalcontext/apicall-not-exist/README.md
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/chainsaw-test.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy-failed.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/gctxentry.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/main-deployment.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/namespace.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/apicall-not-exist/new-deployment.yaml
 create mode 100644 test/conformance/chainsaw/globalcontext/resource-correct/README.md
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/chainsaw-test.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy-succeeded.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/gctxentry.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/main-deployment.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/namespace.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/new-deployment-exists.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-correct/new-deployment.yaml
 create mode 100644 test/conformance/chainsaw/globalcontext/resource-not-exist/README.md
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/chainsaw-test.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy-failed.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/gctxentry.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/main-deployment.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/namespace.yaml
 create mode 100755 test/conformance/chainsaw/globalcontext/resource-not-exist/new-deployment.yaml

diff --git a/api/kyverno/v1/common_types.go b/api/kyverno/v1/common_types.go
index 906572b59f..0d641583c0 100644
--- a/api/kyverno/v1/common_types.go
+++ b/api/kyverno/v1/common_types.go
@@ -97,6 +97,7 @@ type ContextEntry struct {
 	Variable *Variable `json:"variable,omitempty" yaml:"variable,omitempty"`
 
 	// GlobalContextEntryReference is a reference to a cached global context entry.
+	// +kubebuilder:validation:Required
 	GlobalReference *GlobalContextEntryReference `json:"globalReference,omitempty" yaml:"globalReference,omitempty"`
 }
 
diff --git a/api/kyverno/v2alpha1/global_context_entry_types.go b/api/kyverno/v2alpha1/global_context_entry_types.go
index ba142bb591..2273a8c955 100644
--- a/api/kyverno/v2alpha1/global_context_entry_types.go
+++ b/api/kyverno/v2alpha1/global_context_entry_types.go
@@ -142,9 +142,6 @@ type ExternalAPICall struct {
 
 // Validate implements programmatic validation
 func (e *ExternalAPICall) Validate(path *field.Path) (errs field.ErrorList) {
-	if e.Service.URL == "" {
-		errs = append(errs, field.Required(path.Child("url"), "An External API Call entry requires a url"))
-	}
 	if e.RefreshInterval.Duration == 0*time.Second {
 		errs = append(errs, field.Required(path.Child("refreshIntervalSeconds"), "A Resource entry requires a refresh interval greater than 0 seconds"))
 	}
diff --git a/pkg/controllers/webhook/controller.go b/pkg/controllers/webhook/controller.go
index b3f87711cf..d55f175386 100644
--- a/pkg/controllers/webhook/controller.go
+++ b/pkg/controllers/webhook/controller.go
@@ -426,7 +426,7 @@ func (c *controller) reconcileMutatingWebhookConfiguration(ctx context.Context,
 func (c *controller) isGlobalContextEntryReady(name string, gctxentries []*kyvernov2alpha1.GlobalContextEntry) bool {
 	for _, gctxentry := range gctxentries {
 		if gctxentry.Name == name {
-			return gctxentry.Status.Ready
+			return true
 		}
 	}
 	return false
@@ -466,7 +466,7 @@ func (c *controller) updatePolicyStatuses(ctx context.Context) error {
 				for _, ctxEntry := range rule.Context {
 					if ctxEntry.GlobalReference != nil {
 						if !c.isGlobalContextEntryReady(ctxEntry.GlobalReference.Name, gctxentries) {
-							ready, message = false, "Not ready yet"
+							ready, message = false, "global context entry not ready"
 							break
 						}
 					}
diff --git a/pkg/engine/context/loaders/globalcontext.go b/pkg/engine/context/loaders/globalcontext.go
index fb767c6b36..f7c30335b9 100644
--- a/pkg/engine/context/loaders/globalcontext.go
+++ b/pkg/engine/context/loaders/globalcontext.go
@@ -100,9 +100,14 @@ func (g *gctxLoader) loadGctxData() ([]byte, error) {
 		return nil, err
 	}
 
-	jsonData, err := json.Marshal(data)
-	if err != nil {
-		return nil, err
+	var jsonData []byte
+	if _, ok := data.([]byte); ok {
+		jsonData = data.([]byte)
+	} else {
+		jsonData, err = json.Marshal(data)
+		if err != nil {
+			return nil, err
+		}
 	}
 	g.logger.V(6).Info("fetched json data", "name", g.entry.Name, "jsondata", jsonData)
 
diff --git a/pkg/globalcontext/externalapi/entry.go b/pkg/globalcontext/externalapi/entry.go
index 8e3098ac9b..b035074ee0 100644
--- a/pkg/globalcontext/externalapi/entry.go
+++ b/pkg/globalcontext/externalapi/entry.go
@@ -70,6 +70,5 @@ func (e *entry) setData(data any) {
 }
 
 func doCall(ctx context.Context, caller apicall.Caller, call kyvernov1.APICall) (any, error) {
-	// TODO: unmarshall json ?
 	return caller.Execute(ctx, &call)
 }
diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go
index 38c6d09512..1d664c33ba 100644
--- a/pkg/validation/policy/validate.go
+++ b/pkg/validation/policy/validate.go
@@ -680,7 +680,7 @@ func getAllowedVariables(background bool, target bool) *regexp.Regexp {
 
 func addContextVariables(entries []kyvernov1.ContextEntry, ctx *enginecontext.MockContext) {
 	for _, contextEntry := range entries {
-		if contextEntry.APICall != nil || contextEntry.ImageRegistry != nil || contextEntry.Variable != nil {
+		if contextEntry.APICall != nil || contextEntry.GlobalReference != nil || contextEntry.ImageRegistry != nil || contextEntry.Variable != nil {
 			ctx.AddVariable(contextEntry.Name + "*")
 		}
 
@@ -1197,13 +1197,15 @@ func validateRuleContext(rule kyvernov1.Rule) error {
 		}
 
 		var err error
-		if entry.ConfigMap != nil && entry.APICall == nil && entry.ImageRegistry == nil && entry.Variable == nil {
+		if entry.ConfigMap != nil && entry.APICall == nil && entry.GlobalReference == nil && entry.ImageRegistry == nil && entry.Variable == nil {
 			err = validateConfigMap(entry)
-		} else if entry.ConfigMap == nil && entry.APICall != nil && entry.ImageRegistry == nil && entry.Variable == nil {
+		} else if entry.ConfigMap == nil && entry.APICall != nil && entry.GlobalReference == nil && entry.ImageRegistry == nil && entry.Variable == nil {
 			err = validateAPICall(entry)
-		} else if entry.ConfigMap == nil && entry.APICall == nil && entry.ImageRegistry != nil && entry.Variable == nil {
+		} else if entry.ConfigMap == nil && entry.APICall == nil && entry.GlobalReference != nil && entry.ImageRegistry == nil && entry.Variable == nil {
+			err = validateGlobalReference(entry)
+		} else if entry.ConfigMap == nil && entry.APICall == nil && entry.GlobalReference == nil && entry.ImageRegistry != nil && entry.Variable == nil {
 			err = validateImageRegistry(entry)
-		} else if entry.ConfigMap == nil && entry.APICall == nil && entry.ImageRegistry == nil && entry.Variable != nil {
+		} else if entry.ConfigMap == nil && entry.APICall == nil && entry.GlobalReference == nil && entry.ImageRegistry == nil && entry.Variable != nil {
 			err = validateVariable(entry)
 		} else {
 			return fmt.Errorf("exactly one of configMap or apiCall or imageRegistry or variable is required for context entries")
@@ -1311,6 +1313,26 @@ func validateAPICall(entry kyvernov1.ContextEntry) error {
 	return nil
 }
 
+func validateGlobalReference(entry kyvernov1.ContextEntry) error {
+	if entry.GlobalReference == nil {
+		return nil
+	}
+
+	// If JMESPath contains variables, the validation will fail because it's not
+	// possible to infer which value will be inserted by the variable
+	// Skip validation if a variable is detected
+
+	jmesPath := variables.ReplaceAllVars(entry.GlobalReference.JMESPath, func(s string) string { return "kyvernojmespathvariable" })
+
+	if !strings.Contains(jmesPath, "kyvernojmespathvariable") && entry.GlobalReference.JMESPath != "" {
+		if _, err := jmespath.NewParser().Parse(entry.GlobalReference.JMESPath); err != nil {
+			return fmt.Errorf("failed to parse JMESPath %s: %v", entry.GlobalReference.JMESPath, err)
+		}
+	}
+
+	return nil
+}
+
 func validateImageRegistry(entry kyvernov1.ContextEntry) error {
 	if entry.ImageRegistry.Reference == "" {
 		return fmt.Errorf("a ref is required for imageRegistry context entry")
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/README.md b/test/conformance/chainsaw/globalcontext/apicall-correct/README.md
new file mode 100644
index 0000000000..e4830f2597
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/README.md
@@ -0,0 +1,11 @@
+## Description
+
+This test verifies that Global Context Entries are evaluated correctly.
+
+## Expected Behavior
+
+`new-deployment` should be created.
+
+## Reference Issues
+
+
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/chainsaw-test.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/chainsaw-test.yaml
new file mode 100755
index 0000000000..4c74ff5137
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/chainsaw-test.yaml
@@ -0,0 +1,23 @@
+apiVersion: chainsaw.kyverno.io/v1alpha1
+kind: Test
+metadata:
+  creationTimestamp: null
+  name: resource-correct
+spec:
+  steps:
+  - name: scenario
+    try:
+    - apply:
+        file: namespace.yaml
+    - apply:
+        file: main-deployment.yaml
+    - apply:
+        file: gctxentry.yaml
+    - apply:
+        file: clusterpolicy.yaml
+    - apply:
+        file: new-deployment.yaml
+    - assert:
+        file: clusterpolicy-succeeded.yaml
+    - assert:
+        file: new-deployment-exists.yaml
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy-succeeded.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy-succeeded.yaml
new file mode 100755
index 0000000000..47b54ce1f2
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy-succeeded.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+status:
+  conditions:
+  - reason: Succeeded
+    status: "True"
+    type: Ready
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy.yaml
new file mode 100755
index 0000000000..1c459b0a16
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/clusterpolicy.yaml
@@ -0,0 +1,33 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+spec:
+  validationFailureAction: Enforce
+  failurePolicy: Fail
+  rules:
+  - name: main-deployment-exists
+    context:
+    - name: deploymentCount
+      globalReference:
+        name: deployments
+        jmesPath: "items | length(@)"
+    match:
+      all:
+      - resources:
+          kinds:
+          - Pod
+    preconditions:
+      all:
+      - key: '{{ request.operation }}'
+        operator: AnyIn
+        value:
+        - CREATE
+        - UPDATE
+    validate:
+      deny:
+        conditions:
+          any:
+          - key: "{{ deploymentCount }}"
+            operator: Equal
+            value: 0
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/gctxentry.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/gctxentry.yaml
new file mode 100755
index 0000000000..46d1da9f8f
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/gctxentry.yaml
@@ -0,0 +1,8 @@
+apiVersion: kyverno.io/v2alpha1
+kind: GlobalContextEntry
+metadata:
+  name: deployments
+spec:
+  apiCall:
+    urlPath: "/apis/apps/v1/namespaces/test-globalcontext/deployments"
+    refreshInterval: 10s
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/main-deployment.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/main-deployment.yaml
new file mode 100755
index 0000000000..bd269e4252
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/main-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: main-deployment
+  namespace: test-globalcontext
+  labels:
+    app: main-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: main-deployment
+  template:
+    metadata:
+      labels:
+        app: main-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/namespace.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/namespace.yaml
new file mode 100755
index 0000000000..9310828450
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment-exists.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment-exists.yaml
new file mode 100755
index 0000000000..969c3bc053
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment-exists.yaml
@@ -0,0 +1,7 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
diff --git a/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment.yaml b/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment.yaml
new file mode 100755
index 0000000000..343cbc492b
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-correct/new-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: new-deployment
+  template:
+    metadata:
+      labels:
+        app: new-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/README.md b/test/conformance/chainsaw/globalcontext/apicall-not-exist/README.md
new file mode 100644
index 0000000000..8e678c56af
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/README.md
@@ -0,0 +1,11 @@
+## Description
+
+This test verifies that policies are not ready if referenced Global Context Entries don't exist.
+
+## Expected Behavior
+
+`new-deployment` should not be created because the policy is not ready.
+
+## Reference Issues
+
+
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/chainsaw-test.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/chainsaw-test.yaml
new file mode 100755
index 0000000000..5c5984cf95
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/chainsaw-test.yaml
@@ -0,0 +1,26 @@
+apiVersion: chainsaw.kyverno.io/v1alpha1
+kind: Test
+metadata:
+  creationTimestamp: null
+  name: resource-not-exist
+spec:
+  steps:
+  - name: setup
+    try:
+    - apply:
+        file: namespace.yaml
+    - apply:
+        file: main-deployment.yaml
+    - apply:
+        file: gctxentry.yaml
+    - apply:
+        file: clusterpolicy.yaml
+    - assert:
+        file: clusterpolicy-failed.yaml
+  - name: negative
+    try:
+    - apply:
+        expect:
+        - check:
+            ($error != null): true
+        file: new-deployment.yaml
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy-failed.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy-failed.yaml
new file mode 100755
index 0000000000..8e33aa69ed
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy-failed.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+status:
+  conditions:
+  - reason: Failed
+    status: "False"
+    type: Ready
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy.yaml
new file mode 100755
index 0000000000..b1ae1169a3
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/clusterpolicy.yaml
@@ -0,0 +1,33 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+spec:
+  validationFailureAction: Enforce
+  failurePolicy: Fail
+  rules:
+  - name: main-deployment-exists
+    context:
+    - name: deploymentCount
+      globalReference:
+        name: non-existent-reference
+        jmesPath: "items | length(@)"
+    match:
+      all:
+      - resources:
+          kinds:
+          - Pod
+    preconditions:
+      all:
+      - key: '{{ request.operation }}'
+        operator: AnyIn
+        value:
+        - CREATE
+        - UPDATE
+    validate:
+      deny:
+        conditions:
+          any:
+          - key: "{{ deploymentCount }}"
+            operator: Equal
+            value: 0
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/gctxentry.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/gctxentry.yaml
new file mode 100755
index 0000000000..46d1da9f8f
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/gctxentry.yaml
@@ -0,0 +1,8 @@
+apiVersion: kyverno.io/v2alpha1
+kind: GlobalContextEntry
+metadata:
+  name: deployments
+spec:
+  apiCall:
+    urlPath: "/apis/apps/v1/namespaces/test-globalcontext/deployments"
+    refreshInterval: 10s
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/main-deployment.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/main-deployment.yaml
new file mode 100755
index 0000000000..bd269e4252
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/main-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: main-deployment
+  namespace: test-globalcontext
+  labels:
+    app: main-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: main-deployment
+  template:
+    metadata:
+      labels:
+        app: main-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/namespace.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/namespace.yaml
new file mode 100755
index 0000000000..9310828450
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/apicall-not-exist/new-deployment.yaml b/test/conformance/chainsaw/globalcontext/apicall-not-exist/new-deployment.yaml
new file mode 100755
index 0000000000..343cbc492b
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/apicall-not-exist/new-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: new-deployment
+  template:
+    metadata:
+      labels:
+        app: new-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/README.md b/test/conformance/chainsaw/globalcontext/resource-correct/README.md
new file mode 100644
index 0000000000..e4830f2597
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/README.md
@@ -0,0 +1,11 @@
+## Description
+
+This test verifies that Global Context Entries are evaluated correctly.
+
+## Expected Behavior
+
+`new-deployment` should be created.
+
+## Reference Issues
+
+
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/chainsaw-test.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/chainsaw-test.yaml
new file mode 100755
index 0000000000..4c74ff5137
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/chainsaw-test.yaml
@@ -0,0 +1,23 @@
+apiVersion: chainsaw.kyverno.io/v1alpha1
+kind: Test
+metadata:
+  creationTimestamp: null
+  name: resource-correct
+spec:
+  steps:
+  - name: scenario
+    try:
+    - apply:
+        file: namespace.yaml
+    - apply:
+        file: main-deployment.yaml
+    - apply:
+        file: gctxentry.yaml
+    - apply:
+        file: clusterpolicy.yaml
+    - apply:
+        file: new-deployment.yaml
+    - assert:
+        file: clusterpolicy-succeeded.yaml
+    - assert:
+        file: new-deployment-exists.yaml
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy-succeeded.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy-succeeded.yaml
new file mode 100755
index 0000000000..47b54ce1f2
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy-succeeded.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+status:
+  conditions:
+  - reason: Succeeded
+    status: "True"
+    type: Ready
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy.yaml
new file mode 100755
index 0000000000..35e172629e
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/clusterpolicy.yaml
@@ -0,0 +1,33 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+spec:
+  validationFailureAction: Enforce
+  failurePolicy: Fail
+  rules:
+  - name: main-deployment-exists
+    context:
+    - name: deploymentCount
+      globalReference:
+        name: deployments
+        jmesPath: "length(@)"
+    match:
+      all:
+      - resources:
+          kinds:
+          - Pod
+    preconditions:
+      all:
+      - key: '{{ request.operation }}'
+        operator: AnyIn
+        value:
+        - CREATE
+        - UPDATE
+    validate:
+      deny:
+        conditions:
+          any:
+          - key: "{{ deploymentCount }}"
+            operator: Equal
+            value: 0
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/gctxentry.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/gctxentry.yaml
new file mode 100755
index 0000000000..334874bd89
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/gctxentry.yaml
@@ -0,0 +1,10 @@
+apiVersion: kyverno.io/v2alpha1
+kind: GlobalContextEntry
+metadata:
+  name: deployments
+spec:
+  kubernetesResource:
+    group: apps
+    version: v1
+    resource: deployments
+    namespace: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/main-deployment.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/main-deployment.yaml
new file mode 100755
index 0000000000..bd269e4252
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/main-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: main-deployment
+  namespace: test-globalcontext
+  labels:
+    app: main-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: main-deployment
+  template:
+    metadata:
+      labels:
+        app: main-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/namespace.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/namespace.yaml
new file mode 100755
index 0000000000..9310828450
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment-exists.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment-exists.yaml
new file mode 100755
index 0000000000..969c3bc053
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment-exists.yaml
@@ -0,0 +1,7 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
diff --git a/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment.yaml b/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment.yaml
new file mode 100755
index 0000000000..343cbc492b
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-correct/new-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: new-deployment
+  template:
+    metadata:
+      labels:
+        app: new-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/README.md b/test/conformance/chainsaw/globalcontext/resource-not-exist/README.md
new file mode 100644
index 0000000000..8e678c56af
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/README.md
@@ -0,0 +1,11 @@
+## Description
+
+This test verifies that policies are not ready if referenced Global Context Entries don't exist.
+
+## Expected Behavior
+
+`new-deployment` should not be created because the policy is not ready.
+
+## Reference Issues
+
+
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/chainsaw-test.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/chainsaw-test.yaml
new file mode 100755
index 0000000000..5c5984cf95
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/chainsaw-test.yaml
@@ -0,0 +1,26 @@
+apiVersion: chainsaw.kyverno.io/v1alpha1
+kind: Test
+metadata:
+  creationTimestamp: null
+  name: resource-not-exist
+spec:
+  steps:
+  - name: setup
+    try:
+    - apply:
+        file: namespace.yaml
+    - apply:
+        file: main-deployment.yaml
+    - apply:
+        file: gctxentry.yaml
+    - apply:
+        file: clusterpolicy.yaml
+    - assert:
+        file: clusterpolicy-failed.yaml
+  - name: negative
+    try:
+    - apply:
+        expect:
+        - check:
+            ($error != null): true
+        file: new-deployment.yaml
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy-failed.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy-failed.yaml
new file mode 100755
index 0000000000..8e33aa69ed
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy-failed.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+status:
+  conditions:
+  - reason: Failed
+    status: "False"
+    type: Ready
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy.yaml
new file mode 100755
index 0000000000..0c86d8843b
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/clusterpolicy.yaml
@@ -0,0 +1,33 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: namespace-has-coordinator
+spec:
+  validationFailureAction: Enforce
+  failurePolicy: Fail
+  rules:
+  - name: main-deployment-exists
+    context:
+    - name: deploymentCount
+      globalReference:
+        name: non-existent-reference
+        jmesPath: "length(@)"
+    match:
+      all:
+      - resources:
+          kinds:
+          - Pod
+    preconditions:
+      all:
+      - key: '{{ request.operation }}'
+        operator: AnyIn
+        value:
+        - CREATE
+        - UPDATE
+    validate:
+      deny:
+        conditions:
+          any:
+          - key: "{{ deploymentCount }}"
+            operator: Equal
+            value: 0
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/gctxentry.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/gctxentry.yaml
new file mode 100755
index 0000000000..334874bd89
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/gctxentry.yaml
@@ -0,0 +1,10 @@
+apiVersion: kyverno.io/v2alpha1
+kind: GlobalContextEntry
+metadata:
+  name: deployments
+spec:
+  kubernetesResource:
+    group: apps
+    version: v1
+    resource: deployments
+    namespace: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/main-deployment.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/main-deployment.yaml
new file mode 100755
index 0000000000..bd269e4252
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/main-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: main-deployment
+  namespace: test-globalcontext
+  labels:
+    app: main-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: main-deployment
+  template:
+    metadata:
+      labels:
+        app: main-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/namespace.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/namespace.yaml
new file mode 100755
index 0000000000..9310828450
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: test-globalcontext
diff --git a/test/conformance/chainsaw/globalcontext/resource-not-exist/new-deployment.yaml b/test/conformance/chainsaw/globalcontext/resource-not-exist/new-deployment.yaml
new file mode 100755
index 0000000000..343cbc492b
--- /dev/null
+++ b/test/conformance/chainsaw/globalcontext/resource-not-exist/new-deployment.yaml
@@ -0,0 +1,22 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: new-deployment
+  namespace: test-globalcontext
+  labels:
+    app: new-deployment
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: new-deployment
+  template:
+    metadata:
+      labels:
+        app: new-deployment
+    spec:
+      containers:
+      - name: nginx
+        image: nginx:1.14.2
+        ports:
+        - containerPort: 80