# Extend Kyverno's test command to cover generate policy
This is the initial design doc of how we are planning to implement the `test` command for generate policy.
## List of contents
- [What's a generate rule?](#-Generate-rule)
- [How generate policy works?](#How-generate-policy-works)
- [Test command for generate policy](#Test-command-for-generate-policy)
- [Structure of `test.yaml`](#Structure-of-testyaml)
- [Initial Prototype](#Initial-Prototype)
## <a name="intro">Generate rule</a>
_A **generate rule** can be used to create additional resources when a new resource is created or when the source is updated. This is useful to create supporting resources, such as new RoleBindings or NetworkPolicies for a Namespace._
## <a name="how-it-works">How generate policy works?</a>
Let us first see how the generate policy works by the help of an example.
Let's take [Add Quota](https://kyverno.io/policies/best-practices/add_ns_quota/) policy for instance:
* Set-up and run kyverno in a local cluster since the `generate policy` can only be applied to a live cluster.
* You can find instructions on how to run kyverno in development mode [here](https://github.com/kyverno/kyverno/wiki/Running-in-development-mode)
* Suppose we have the policy definition in `addQuota.yaml` file. Let's apply it to the cluster:
```
kubectl apply -f addQuota.yaml
```
* Now, let's create a demo namespace
```
kubectl create ns demo
```
* Now, search for the resources listed in `addQuota.yaml` that are meant to be generated (here, `limitranges` and `resourcequotas`)
```
kubectl get limitranges --all-namespaces
kubectl get resourcequotas --all-namespaces
```
* You'll get the resources being listed on your terminal.
## <a name="test-command">Test command for generate policy</a>
_Now, coming to the test command for **Generate Policy**. We can go about it in two ways:_
1. If the user doesn't provide the **desired resource**, then we can just display if kyverno can generate the resource or not
2. If the user provides the **resource manifests** then we can compare it with the kyverno created resource is a subset of the user provided resource(in terms of tags). If that pass, test success else failure.
_The next part is how should we log the result?_
So if both the resources match, we can update the test results to `pass` or else `fail`.
## <a name="test-yaml">Structure of `test.yaml`</a>
_To demonstrate this, let us take [Add Network Policy](https://kyverno.io/policies/best-practices/add_network_policy/) \& [Add Quota](https://kyverno.io/policies/best-practices/add_ns_quota/) policy for example._
1. When the user doesn't provide the desired resource
```yaml
name: add-networkpolicy
policies:
- add_network_policy.yaml
resources:
- resource.yaml
results:
- policy: add-networkpolicy
rule: default-deny
kind: Namespace
resource: demo
result: pass
```
```yaml
# What if the NetworkPolicy was only applicable to Pods
name: add-networkpolicy
policies:
- add_network_policy.yaml
resources:
- resource.yaml
results:
- policy: add-networkpolicy
rule: default-deny
kind: Pod
resource: demo # name of resource(Namespace, Pod, etc)
namespace: testing # namespace where the target resource is being created
result: pass
```
```yaml
name: add-ns-quota
policies:
- add_ns_quota.yaml
resources:
- resource.yaml
results:
- policy: add-ns-quota
rule: generate-resourcequota
kind: Namespace # source
resource: demo # target
result: pass
- policy: add-ns-quota
rule: generate-limitrange
kind: Namespace
resource: demo
result: pass
```
2. When the user provides the desired resource
```yaml
name: add-networkpolicy
policies:
- add_network_policy.yaml
resources:
- resource.yaml
results:
- policy: add-networkpolicy
generatedResource: generatedResource.yaml
rule: default-deny
kind: Namespace
resource: demo
result: pass
```
```yaml
name: add-ns-quota
policies:
- add_ns_quota.yaml
resources:
- resource.yaml
results:
- policy: add-ns-quota
rule: generate-resourcequota
generatedResource: generatedResource1.yaml
kind: Namespace
resource: demo
result: pass
- policy: add-ns-quota
rule: generate-limitrange
generatedResource: generatedResource2.yaml
kind: Namespace
resource: demo
result: pass
```
## <a name="initial-prototype">Initial Prototype</a>
- We would have to add a new field i.e. `generatedResource` in the [TestResults](https://github.com/kyverno/kyverno/blob/50cb1859c39abd900315326f22e46e20942e8644/pkg/kyverno/test/test_command.go#L176) struct.
- Then we'll extract the `test.yaml` from the path provided by the user in the form of bytes by executing the test command in cli.
- Next we'll extract the `policy` by passing the above extracted result into [`applyPoliciesFromPath`](https://github.com/kyverno/kyverno/blob/e5e849acfebd0461ae147d066821fdf86c620fc7/pkg/kyverno/test/test_command.go#L575) in `pkg/kyverno/test/test_command.go`.
- Inside `applyPoliciesFromPath` we call [`getFullPath`](https://github.com/kyverno/kyverno/blob/50cb1859c39abd900315326f22e46e20942e8644/pkg/kyverno/test/test_command.go#L562) to update the path of `generatedResource` in the fields.
- Also, we've to call `GetPoliciesFromPaths` in `pkg/kyverno/common/common.go` to get the policies
- Next, we are going to loop through all the resources and invoke [`ApplyPolicyOnResource`](https://github.com/kyverno/kyverno/blob/5c50191d8a8ab8e3863d06531679a124a4d81931/pkg/kyverno/common/common.go#L459) to apply a policy on the resource. Inside `ApplyPolicyOnResource`:
- we'll make a policy context
- call the `engine.Generate()` by passing the context in it and store the set of applicable rules in `engineResponses` for generating the resource
- Next, we can create a standalone function to create the GR spec from the given policy and resource manifests and the `enigneResponse`.
(for instance the [`transform()`](https://github.com/kyverno/kyverno/blob/17e671bf53c37ea7d77e1eea45263df8842e2c5d/pkg/webhooks/generation.go#L443) function)
- Then we'll be creating an abstraction using [`processGR()`](https://github.com/kyverno/kyverno/blob/50cb1859c39abd900315326f22e46e20942e8644/pkg/generate/generate.go#L32) to process the GRs.
- [**Update-1**: Since the `applyGeneratePolicy()` is a function with receiver type `Controller` and uses the client, we've to create an abstraction for it.]
- [**Update-2**: We can leverage the `applyGeneratePolicy()` by using the [client-go/kubernetes/fake](https://pkg.go.dev/k8s.io/client-go/kubernetes/fake) package. So, no need of creating an abstraction.]
- Finally, we get the result from the above.
- If the user hasn't provided the resource manifests then we'll update the `TestResults` struct according to the result obtained.
- If the user has provided the resource manifests, we can compare the `GeneratedResource` with the `engineResponse` and update the `TestResults` struct.
> **Note**: No logs should be added to the default log level.