-
Notifications
You must be signed in to change notification settings - Fork 237
Description
A weakness of execution configuration pattern is that it interleaves configuration and code. For example, gatekeeper constraint templates wrap Rego code in KRM. Similarly, starlark code is wrapped in StarlarkRun resource. We can improve the development UX by providing an imperative pre-processor function that injects the content of a file into a particular field of a KRM resource. Note that this is useful for gatekeeper admission controller, even when not using the gatekeeper kpt function.
Strawman:
Assume one or more resources (which may be deeply nested) in the current directory have a comment directive pointing to a file containing code, e.g.:
apiVersion: fn.kpt.dev/v1alpha1
kind: StarlarkRun
metadata:
name: set-namespace-to-prod
# kpt-include: myscript.star
source: |
...
This script references myscript.star
in the same directory:
def setnamespace(resources, namespace):
for resource in resources:
# mutate the resource
resource["metadata"]["namespace"] = namespace
setnamespace(ctx.resource_list["items"], "prod")
And we have a gatekeeper template:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata: # kpt-merge: /k8sbannedconfigmapkeysv1
name: k8sbannedconfigmapkeysv1
spec:
crd:
spec:
names:
kind: K8sBannedConfigMapKeysV1
validation:
openAPIV3Schema:
properties:
keys:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
# kpt-include: myscript.rego
rego: |
...
That references myscript.rego
:
package ban_keys
violation[{"msg": sprintf("%v", [val])}] {
keys = {key | input.review.object.data[key]}
banned = {key | input.parameters.keys[_] = key}
overlap = keys & banned
count(overlap) > 0
val := sprintf("The following banned keys are being used in the ConfigMap: %v", [overlap])
}
After running the imperative function:
$ kpt fn eval -i gcr.io/kpt-fn/include-file --mount-current
(See #2351)
All resources containing the kpt-include
comment directive are expanded:
apiVersion: fn.kpt.dev/v1alpha1
kind: StarlarkRun
metadata:
name: set-namespace-to-prod
# kpt-include: myscript.star
source: |
# set the namespace on all resources
def setnamespace(resources, namespace):
for resource in resources:
# mutate the resource
resource["metadata"]["namespace"] = namespace
setnamespace(ctx.resource_list["items"], "prod")
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata: # kpt-merge: /k8sbannedconfigmapkeysv1
name: k8sbannedconfigmapkeysv1
spec:
crd:
spec:
names:
kind: K8sBannedConfigMapKeysV1
validation:
openAPIV3Schema:
properties:
keys:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
# kpt-include: myscript.rego
rego: |-
package ban_keys
violation[{"msg": sprintf("%v", [val])}] {
keys = {key | input.review.object.data[key]}
banned = {key | input.parameters.keys[_] = key}
overlap = keys & banned
count(overlap) > 0
val := sprintf("The following banned keys are being used in the ConfigMap: %v", [overlap])
}
Considerations:
- Potential for abuse of this function. We don't want to encourage its use for maintaining partial resource configuration as a DRY technique (i.e. injecting KRM into KRM). Injecting into a single field of type string mitigates this. Should also consider a more descriptive name.
- Whether to support relative paths or only allow the file in the same directory as the KRM. The latter is fine to start with to keep things simple.