mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-08 10:04:25 +00:00
merge main
Signed-off-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
commit
0bb35aa302
79 changed files with 2437 additions and 486 deletions
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
|
@ -113,6 +113,16 @@ jobs:
|
|||
run: |
|
||||
make docker-build-kyverno
|
||||
|
||||
- name: Trivy Scan Image
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'ghcr.io/kyverno/kyverno:latest'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
build-kyverno-cli:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre-checks
|
||||
|
|
32
.github/workflows/image.yaml
vendored
32
.github/workflows/image.yaml
vendored
|
@ -18,6 +18,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: login to GitHub Container Registry
|
||||
run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
|
@ -31,6 +36,11 @@ jobs:
|
|||
run: |
|
||||
make docker-publish-initContainer
|
||||
|
||||
- name: Sign image
|
||||
run: |
|
||||
KYVERNO_IMAGE_VERSION=$(git describe --match "v[0-9]*")
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyvernopre:${KYVERNO_IMAGE_VERSION}
|
||||
|
||||
push-kyverno:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -45,6 +55,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: login to GitHub Container Registry
|
||||
run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
|
@ -58,6 +73,11 @@ jobs:
|
|||
run: |
|
||||
make docker-publish-kyverno
|
||||
|
||||
- name: Sign image
|
||||
run: |
|
||||
KYVERNO_IMAGE_VERSION=$(git describe --match "v[0-9]*")
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyverno:${KYVERNO_IMAGE_VERSION}
|
||||
|
||||
push-kyverno-cli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -72,6 +92,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: login to GitHub Container Registry
|
||||
run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
|
@ -83,4 +108,9 @@ jobs:
|
|||
|
||||
- name: docker images publish
|
||||
run: |
|
||||
make docker-publish-cli
|
||||
make docker-publish-cli
|
||||
|
||||
- name: Sign image
|
||||
run: |
|
||||
KYVERNO_IMAGE_VERSION=$(git describe --match "v[0-9]*")
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyverno-cli:${KYVERNO_IMAGE_VERSION}
|
||||
|
|
62
.github/workflows/release.yaml
vendored
62
.github/workflows/release.yaml
vendored
|
@ -17,6 +17,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
|
@ -40,10 +45,18 @@ jobs:
|
|||
with:
|
||||
install: true
|
||||
|
||||
- name: Set version
|
||||
run: |
|
||||
echo "KYVERNO_VERSION=$(git describe --match "v[0-9]*")"
|
||||
|
||||
- name : docker images publish
|
||||
run: |
|
||||
make docker-publish-initContainer
|
||||
|
||||
- name: Sign image
|
||||
run: |
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyvernopre:${KYVERNO_VERSION}
|
||||
|
||||
release-kyverno:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -57,6 +70,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
|
@ -80,10 +98,41 @@ jobs:
|
|||
with:
|
||||
install: true
|
||||
|
||||
- name: Set version
|
||||
run: |
|
||||
echo "KYVERNO_VERSION=$(git describe --match "v[0-9]*")"
|
||||
|
||||
- name: Generate SBOM JSON
|
||||
uses: CycloneDX/gh-gomod-generate-sbom@v0.3.0
|
||||
with:
|
||||
json: true
|
||||
output: kyverno-v${{ env.KYVERNO_VERSION }}-bom.cdx.json
|
||||
resolve-licenses: true
|
||||
version: ^v0
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: kyverno-bom-cdx
|
||||
path: kyverno-v*-bom.cdx.json
|
||||
|
||||
- name : docker images publish
|
||||
run: |
|
||||
make docker-publish-kyverno
|
||||
|
||||
- name: Sign image and SBOM
|
||||
run: |
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyverno:${KYVERNO_VERSION}
|
||||
cosign attach sbom -sbom ./*-bom.cdx.json -type cyclonedx ghcr.io/kyverno/kyverno:latest
|
||||
|
||||
- name: Trivy Scan Image
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'ghcr.io/kyverno/kyverno:latest'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
release-kyverno-cli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -97,6 +146,11 @@ jobs:
|
|||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.2.1'
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
|
@ -120,9 +174,17 @@ jobs:
|
|||
with:
|
||||
install: true
|
||||
|
||||
- name: Set version
|
||||
run: |
|
||||
echo "KYVERNO_VERSION=$(git describe --match "v[0-9]*")"
|
||||
|
||||
- name : docker images publish
|
||||
run: |
|
||||
make docker-publish-cli
|
||||
|
||||
- name: Sign image
|
||||
run: |
|
||||
echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY_PASSWORD }}" | cosign sign -key <(echo -n "${{ secrets.KYVERNO_COSIGN_PRIVATE_KEY }}") ghcr.io/kyverno/kyverno-cli:${KYVERNO_VERSION}
|
||||
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
## v1.5.0-rc1
|
||||
### Note
|
||||
- With the change of dynamic webhooks, the readiness of the policy is reflected by `.status.ready`, When ready, it means the policy is ready to serve the admission requests.
|
||||
|
||||
### Deprecation
|
||||
- To add a consistent style in flag names the following flags have been deprecated `webhooktimeout`, `gen-workers`,`disable-metrics`, `background-scan`, `auto-update-webhooks` these will be removed in 1.6.0. The new flags are `webhookTimeout`, `genWorkers`, `disablMetrics`, `backgroundScan`, `autoUpdateWebhooks`.
|
||||
|
||||
## v1.4.3
|
||||
|
||||
## v1.4.3-rc2
|
||||
|
|
1
Makefile
1
Makefile
|
@ -202,6 +202,7 @@ test-e2e-local:
|
|||
#Test TestCmd Policy
|
||||
test-cmd: cli
|
||||
$(PWD)/$(CLI_PATH)/kyverno test https://github.com/kyverno/policies/main
|
||||
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-mutate
|
||||
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test
|
||||
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-policy && exit 1 || exit 0
|
||||
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-rule && exit 1 || exit 0
|
||||
|
|
|
@ -92,6 +92,9 @@ The following table lists the configurable parameters of the kyverno chart and t
|
|||
| `podAnnotations` | annotations to add to each pod | `{}` |
|
||||
| `podLabels` | additional labels to add to each pod | `{}` |
|
||||
| `podSecurityContext` | security context for the pod | `{}` |
|
||||
| `podDisruptionBudget.enabled` | Adds a PodDisruptionBudget for the kyverno deployment | `false` |
|
||||
| `podDisruptionBudget.minAvailable` | Configures the minimum available pods for kyverno disruptions. Cannot used if `maxUnavailable` is set. | `0` |
|
||||
| `podDisruptionBudget.maxUnavailable` | Configures the maximum unavailable pods for kyverno disruptions. Cannot used if `minAvailable` is set. | `nil` |
|
||||
| `priorityClassName` | priorityClassName | `nil` |
|
||||
| `rbac.create` | create ClusterRoles, ClusterRoleBindings, and ServiceAccount | `true` |
|
||||
| `rbac.serviceAccount.create` | create a ServiceAccount | `true` |
|
||||
|
|
|
@ -91,3 +91,16 @@ app.kubernetes.io/instance: {{ .Release.Name }}
|
|||
{{ default "default" .Values.rbac.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Create the default PodDisruptionBudget to use */}}
|
||||
{{- define "podDisruptionBudget.spec" -}}
|
||||
{{- if and .Values.podDisruptionBudget.minAvailable .Values.podDisruptionBudget.maxUnavailable }}
|
||||
{{- fail "Cannot set both .Values.podDisruptionBudget.minAvailable and .Values.podDisruptionBudget.maxUnavailable" -}}
|
||||
{{- end }}
|
||||
{{- if not .Values.podDisruptionBudget.maxUnavailable }}
|
||||
minAvailable: {{ default 0 .Values.podDisruptionBudget.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -33,6 +33,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -576,7 +577,9 @@ spec:
|
|||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified.
|
||||
the resource being created or modified. Specifying ResourceDescription
|
||||
directly under exclude is being deprecated. Please specify
|
||||
under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1250,7 +1253,9 @@ spec:
|
|||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified. Requires at least
|
||||
one tag to be specified when under MatchResources.
|
||||
one tag to be specified when under MatchResources. Specifying
|
||||
ResourceDescription directly under match is being deprecated.
|
||||
Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1812,10 +1817,11 @@ spec:
|
|||
in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for
|
||||
this policy. After the timeout passes, the admission request will
|
||||
fail based on the failure policy. The default timeout is 3s, the
|
||||
value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds
|
||||
allowed to apply this policy. After the configured time expires,
|
||||
the admission request may fail, or may simply ignore the policy
|
||||
results, based on the failure policy. The default timeout is 10s,
|
||||
the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
@ -3439,6 +3445,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -3983,7 +3990,9 @@ spec:
|
|||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified.
|
||||
the resource being created or modified. Specifying ResourceDescription
|
||||
directly under exclude is being deprecated. Please specify
|
||||
under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -4657,7 +4666,9 @@ spec:
|
|||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified. Requires at least
|
||||
one tag to be specified when under MatchResources.
|
||||
one tag to be specified when under MatchResources. Specifying
|
||||
ResourceDescription directly under match is being deprecated.
|
||||
Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -5219,10 +5230,11 @@ spec:
|
|||
in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for
|
||||
this policy. After the timeout passes, the admission request will
|
||||
fail based on the failure policy. The default timeout is 3s, the
|
||||
value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds
|
||||
allowed to apply this policy. After the configured time expires,
|
||||
the admission request may fail, or may simply ignore the policy
|
||||
results, based on the failure policy. The default timeout is 10s,
|
||||
the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
|
14
charts/kyverno/templates/poddisruptionbudget.yaml
Normal file
14
charts/kyverno/templates/poddisruptionbudget.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
{{- if .Values.podDisruptionBudget.enabled }}
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ template "kyverno.fullname" . }}
|
||||
labels: {{ include "kyverno.labels" . | nindent 4 }}
|
||||
app: kyverno
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
spec:
|
||||
{{- include "podDisruptionBudget.spec" . | indent 2 }}
|
||||
selector:
|
||||
matchLabels: {{ include "kyverno.matchLabels" . | nindent 6 }}
|
||||
app: kyverno
|
||||
{{- end }}
|
|
@ -32,7 +32,7 @@ testImage:
|
|||
repository:
|
||||
# testImage.tag defaults to "latest" if omitted
|
||||
tag:
|
||||
# testImage.pullPolicy defaults to image.pullPolicy if ommitted
|
||||
# testImage.pullPolicy defaults to image.pullPolicy if omitted
|
||||
pullPolicy:
|
||||
|
||||
replicaCount: 1
|
||||
|
@ -52,6 +52,14 @@ antiAffinity:
|
|||
# Changing this to a region would allow you to spread pods across regions
|
||||
topologyKey: "kubernetes.io/hostname"
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
# minAvailable: 1
|
||||
# maxUnavailable: 1
|
||||
|
||||
# minAvailable and maxUnavailable can either be set to an integer (e.g. 1)
|
||||
# or a percentage value (e.g. 25%)
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
|
||||
|
@ -72,7 +80,7 @@ envVarsInit: {}
|
|||
envVars: {}
|
||||
|
||||
extraArgs: []
|
||||
# - --webhooktimeout=4
|
||||
# - --webhookTimeout=4
|
||||
|
||||
resources:
|
||||
limits:
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/cosign"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
"k8s.io/klog/v2"
|
||||
|
@ -23,6 +21,7 @@ import (
|
|||
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/cosign"
|
||||
dclient "github.com/kyverno/kyverno/pkg/dclient"
|
||||
event "github.com/kyverno/kyverno/pkg/event"
|
||||
"github.com/kyverno/kyverno/pkg/generate"
|
||||
|
@ -59,6 +58,7 @@ var (
|
|||
genWorkers int
|
||||
profile bool
|
||||
disableMetricsExport bool
|
||||
autoUpdateWebhooks bool
|
||||
policyControllerResyncPeriod time.Duration
|
||||
imagePullSecrets string
|
||||
imageSignatureRepository string
|
||||
|
@ -71,17 +71,28 @@ func main() {
|
|||
flag.StringVar(&filterK8sResources, "filterK8sResources", "", "Resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. For example, --filterK8sResources \"[Deployment, kyverno, kyverno],[Events, *, *]\"")
|
||||
flag.StringVar(&excludeGroupRole, "excludeGroupRole", "", "")
|
||||
flag.StringVar(&excludeUsername, "excludeUsername", "", "")
|
||||
flag.IntVar(&webhookTimeout, "webhooktimeout", 3, "Timeout for webhook configurations")
|
||||
flag.IntVar(&genWorkers, "gen-workers", 10, "Workers for generate controller")
|
||||
// deprecated
|
||||
flag.IntVar(&webhookTimeout, "webhooktimeout", int(webhookconfig.DefaultWebhookTimeout), "Timeout for webhook configurations. Deprecated and will be removed in 1.6.0.")
|
||||
flag.IntVar(&webhookTimeout, "webhookTimeout", int(webhookconfig.DefaultWebhookTimeout), "Timeout for webhook configurations.")
|
||||
// deprecated
|
||||
flag.IntVar(&genWorkers, "gen-workers", 10, "Workers for generate controller. Deprecated and will be removed in 1.6.0. ")
|
||||
flag.IntVar(&genWorkers, "genWorkers", 10, "Workers for generate controller")
|
||||
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
||||
flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
|
||||
flag.BoolVar(&profile, "profile", false, "Set this flag to 'true', to enable profiling.")
|
||||
flag.StringVar(&profilePort, "profile-port", "6060", "Enable profiling at given port, defaults to 6060.")
|
||||
flag.BoolVar(&disableMetricsExport, "disable-metrics", false, "Set this flag to 'true', to enable exposing the metrics.")
|
||||
// deprecated
|
||||
flag.BoolVar(&disableMetricsExport, "disable-metrics", false, "Set this flag to 'true', to enable exposing the metrics. Deprecated and will be removed in 1.6.0. ")
|
||||
flag.BoolVar(&disableMetricsExport, "disableMetrics", false, "Set this flag to 'true', to enable exposing the metrics.")
|
||||
flag.StringVar(&metricsPort, "metrics-port", "8000", "Expose prometheus metrics at the given port, default to 8000.")
|
||||
flag.DurationVar(&policyControllerResyncPeriod, "background-scan", time.Hour, "Perform background scan every given interval, e.g., 30s, 15m, 1h.")
|
||||
// deprecated
|
||||
flag.DurationVar(&policyControllerResyncPeriod, "background-scan", time.Hour, "Perform background scan every given interval, e.g., 30s, 15m, 1h. Deprecated and will be removed in 1.6.0. ")
|
||||
flag.DurationVar(&policyControllerResyncPeriod, "backgroundScan", time.Hour, "Perform background scan every given interval, e.g., 30s, 15m, 1h.")
|
||||
flag.StringVar(&imagePullSecrets, "imagePullSecrets", "", "Secret resource names for image registry access credentials.")
|
||||
flag.StringVar(&imageSignatureRepository, "imageSignatureRepository", "", "Alternate repository for image signatures. Can be overridden per rule via `verifyImages.Repository`.")
|
||||
// deprecated
|
||||
flag.BoolVar(&autoUpdateWebhooks, "auto-update-webhooks", true, "Set this flag to 'false' to disable auto-configuration of the webhook. Deprecated and will be removed in 1.6.0.")
|
||||
flag.BoolVar(&autoUpdateWebhooks, "autoUpdateWebhooks", true, "Set this flag to 'false' to disable auto-configuration of the webhook.")
|
||||
|
||||
if err := flag.Set("v", "2"); err != nil {
|
||||
setupLog.Error(err, "failed to set log level")
|
||||
|
@ -218,10 +229,15 @@ func main() {
|
|||
webhookCfg := webhookconfig.NewRegister(
|
||||
clientConfig,
|
||||
client,
|
||||
pclient,
|
||||
rCache,
|
||||
pInformer.Kyverno().V1().ClusterPolicies(),
|
||||
pInformer.Kyverno().V1().Policies(),
|
||||
serverIP,
|
||||
int32(webhookTimeout),
|
||||
debug,
|
||||
autoUpdateWebhooks,
|
||||
stopCh,
|
||||
log.Log)
|
||||
|
||||
webhookMonitor, err := webhookconfig.NewMonitor(kubeClient, log.Log.WithName("WebhookMonitor"))
|
||||
|
@ -381,7 +397,9 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
go webhookCfg.UpdateWebhookConfigurations(configData)
|
||||
if !autoUpdateWebhooks {
|
||||
go webhookCfg.UpdateWebhookConfigurations(configData)
|
||||
}
|
||||
if registrationErr := registerWrapperRetry(); registrationErr != nil {
|
||||
setupLog.Error(err, "Timeout registering admission control webhooks")
|
||||
os.Exit(1)
|
||||
|
|
4
cosign.pub
Normal file
4
cosign.pub
Normal file
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExxWHpvn2uMYqg174TmTcnGELOXXM
|
||||
7/cGqLZW88FFceihl1WA24yKxtMBZqw/s06XqPqujqRzhkaSKa2zkRUWUA==
|
||||
-----END PUBLIC KEY-----
|
|
@ -27,6 +27,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -570,7 +571,9 @@ spec:
|
|||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified.
|
||||
the resource being created or modified. Specifying ResourceDescription
|
||||
directly under exclude is being deprecated. Please specify
|
||||
under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1244,7 +1247,9 @@ spec:
|
|||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified. Requires at least
|
||||
one tag to be specified when under MatchResources.
|
||||
one tag to be specified when under MatchResources. Specifying
|
||||
ResourceDescription directly under match is being deprecated.
|
||||
Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1806,10 +1811,11 @@ spec:
|
|||
in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for
|
||||
this policy. After the timeout passes, the admission request will
|
||||
fail based on the failure policy. The default timeout is 3s, the
|
||||
value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds
|
||||
allowed to apply this policy. After the configured time expires,
|
||||
the admission request may fail, or may simply ignore the policy
|
||||
results, based on the failure policy. The default timeout is 10s,
|
||||
the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
|
|
@ -27,6 +27,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -571,7 +572,9 @@ spec:
|
|||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified.
|
||||
the resource being created or modified. Specifying ResourceDescription
|
||||
directly under exclude is being deprecated. Please specify
|
||||
under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1245,7 +1248,9 @@ spec:
|
|||
resources:
|
||||
description: ResourceDescription contains information about
|
||||
the resource being created or modified. Requires at least
|
||||
one tag to be specified when under MatchResources.
|
||||
one tag to be specified when under MatchResources. Specifying
|
||||
ResourceDescription directly under match is being deprecated.
|
||||
Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1807,10 +1812,11 @@ spec:
|
|||
in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for
|
||||
this policy. After the timeout passes, the admission request will
|
||||
fail based on the failure policy. The default timeout is 3s, the
|
||||
value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds
|
||||
allowed to apply this policy. After the configured time expires,
|
||||
the admission request may fail, or may simply ignore the policy
|
||||
results, based on the failure policy. The default timeout is 10s,
|
||||
the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
|
|
@ -45,6 +45,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -374,7 +375,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Specifying ResourceDescription directly under exclude is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -781,7 +782,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources. Specifying ResourceDescription directly under match is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1138,7 +1139,7 @@ spec:
|
|||
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for this policy. After the timeout passes, the admission request will fail based on the failure policy. The default timeout is 3s, the value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
@ -2363,6 +2364,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -2692,7 +2694,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Specifying ResourceDescription directly under exclude is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -3099,7 +3101,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources. Specifying ResourceDescription directly under match is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -3456,7 +3458,7 @@ spec:
|
|||
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for this policy. After the timeout passes, the admission request will fail based on the failure policy. The default timeout is 3s, the value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
@ -5089,3 +5091,23 @@ spec:
|
|||
securityContext:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: kyverno-service-account
|
||||
---
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
labels:
|
||||
app: kyverno
|
||||
app.kubernetes.io/component: kyverno
|
||||
app.kubernetes.io/instance: kyverno
|
||||
app.kubernetes.io/managed-by: Kustomize
|
||||
app.kubernetes.io/name: kyverno
|
||||
app.kubernetes.io/part-of: kyverno
|
||||
app.kubernetes.io/version: v1.4.3
|
||||
name: kyverno
|
||||
namespace: kyverno
|
||||
spec:
|
||||
minAvailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kyverno
|
||||
app.kubernetes.io/name: kyverno
|
||||
|
|
|
@ -32,6 +32,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -361,7 +362,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Specifying ResourceDescription directly under exclude is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -768,7 +769,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources. Specifying ResourceDescription directly under match is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -1125,7 +1126,7 @@ spec:
|
|||
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for this policy. After the timeout passes, the admission request will fail based on the failure policy. The default timeout is 3s, the value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
@ -2322,6 +2323,7 @@ spec:
|
|||
type: string
|
||||
- jsonPath: .spec.failurePolicy
|
||||
name: Failure Policy
|
||||
priority: 1
|
||||
type: string
|
||||
- jsonPath: .status.ready
|
||||
name: Ready
|
||||
|
@ -2651,7 +2653,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Specifying ResourceDescription directly under exclude is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -3058,7 +3060,7 @@ spec:
|
|||
type: string
|
||||
type: array
|
||||
resources:
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources.
|
||||
description: ResourceDescription contains information about the resource being created or modified. Requires at least one tag to be specified when under MatchResources. Specifying ResourceDescription directly under match is being deprecated. Please specify under "any" or "all" instead.
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
|
@ -3415,7 +3417,7 @@ spec:
|
|||
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
|
||||
type: string
|
||||
webhookTimeoutSeconds:
|
||||
description: WebhookTimeoutSeconds specifies the webhook timeout for this policy. After the timeout passes, the admission request will fail based on the failure policy. The default timeout is 3s, the value must be between 1 and 30 seconds. Default to 10 seconds.
|
||||
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
|
@ -4782,3 +4784,17 @@ spec:
|
|||
selector:
|
||||
app: kyverno
|
||||
app.kubernetes.io/name: kyverno
|
||||
---
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
labels:
|
||||
app: kyverno
|
||||
name: kyverno
|
||||
namespace: kyverno
|
||||
spec:
|
||||
minAvailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kyverno
|
||||
app.kubernetes.io/name: kyverno
|
||||
|
|
|
@ -8,3 +8,4 @@ resources:
|
|||
- ./metricsconfigmap.yaml
|
||||
- ./service.yaml
|
||||
- ./serviceaccount.yaml
|
||||
- ./poddisruptionbudget.yaml
|
||||
|
|
14
definitions/k8s-resource/poddisruptionbudget.yaml
Normal file
14
definitions/k8s-resource/poddisruptionbudget.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: kyverno
|
||||
labels:
|
||||
app: kyverno
|
||||
namespace: kyverno
|
||||
spec:
|
||||
minAvailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kyverno
|
||||
# do not remove
|
||||
app.kubernetes.io/name: kyverno
|
|
@ -65,11 +65,11 @@ spec:
|
|||
args:
|
||||
- "--filterK8sResources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*][ReportChangeRequest,*,*][ClusterReportChangeRequest,*,*][PolicyReport,*,*][ClusterPolicyReport,*,*]"
|
||||
# customize webhook timeout
|
||||
#- "--webhooktimeout=4"
|
||||
#- "--webhookTimeout=4"
|
||||
# enable profiling
|
||||
# - "--profile"
|
||||
# configure the workers for generate controller
|
||||
# - --gen-workers=20
|
||||
# - --genWorkers=20
|
||||
- "-v=2"
|
||||
ports:
|
||||
- containerPort: 9443
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// +kubebuilder:resource:path=clusterpolicies,scope="Cluster",shortName=cpol
|
||||
// +kubebuilder:printcolumn:name="Background",type="string",JSONPath=".spec.background"
|
||||
// +kubebuilder:printcolumn:name="Action",type="string",JSONPath=".spec.validationFailureAction"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
type ClusterPolicy struct {
|
||||
metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
|
|
|
@ -22,7 +22,7 @@ type PolicyList struct {
|
|||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Background",type="string",JSONPath=".spec.background"
|
||||
// +kubebuilder:printcolumn:name="Action",type="string",JSONPath=".spec.validationFailureAction"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
// +kubebuilder:resource:shortName=pol
|
||||
type Policy struct {
|
||||
|
@ -68,10 +68,9 @@ type Spec struct {
|
|||
// +optional
|
||||
SchemaValidation *bool `json:"schemaValidation,omitempty" yaml:"schemaValidation,omitempty"`
|
||||
|
||||
// WebhookTimeoutSeconds specifies the webhook timeout for this policy.
|
||||
// After the timeout passes, the admission request will fail based on the failure policy.
|
||||
// The default timeout is 3s, the value must be between 1 and 30 seconds.
|
||||
// Default to 10 seconds.
|
||||
// WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy.
|
||||
// After the configured time expires, the admission request may fail, or may simply ignore the policy results,
|
||||
// based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
|
||||
WebhookTimeoutSeconds *int32 `json:"webhookTimeoutSeconds,omitempty" yaml:"webhookTimeoutSeconds,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -267,11 +266,15 @@ type MatchResources struct {
|
|||
All ResourceFilters `json:"all,omitempty" yaml:"all,omitempty"`
|
||||
|
||||
// UserInfo contains information about the user performing the operation.
|
||||
// Specifying UserInfo directly under match is being deprecated.
|
||||
// Please specify under "any" or "all" instead.
|
||||
// +optional
|
||||
UserInfo `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// ResourceDescription contains information about the resource being created or modified.
|
||||
// Requires at least one tag to be specified when under MatchResources.
|
||||
// Specifying ResourceDescription directly under match is being deprecated.
|
||||
// Please specify under "any" or "all" instead.
|
||||
// +optional
|
||||
ResourceDescription `json:"resources,omitempty" yaml:"resources,omitempty"`
|
||||
}
|
||||
|
@ -288,10 +291,14 @@ type ExcludeResources struct {
|
|||
All ResourceFilters `json:"all,omitempty" yaml:"all,omitempty"`
|
||||
|
||||
// UserInfo contains information about the user performing the operation.
|
||||
// Specifying UserInfo directly under exclude is being deprecated.
|
||||
// Please specify under "any" or "all" instead.
|
||||
// +optional
|
||||
UserInfo `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// ResourceDescription contains information about the resource being created or modified.
|
||||
// Specifying ResourceDescription directly under exclude is being deprecated.
|
||||
// Please specify under "any" or "all" instead.
|
||||
// +optional
|
||||
ResourceDescription `json:"resources,omitempty" yaml:"resources,omitempty"`
|
||||
}
|
||||
|
|
|
@ -38,6 +38,28 @@ func (p *ClusterPolicy) HasMutate() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// HasValidate checks for validate rule types
|
||||
func (p *ClusterPolicy) HasValidate() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if rule.HasValidate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasGenerate checks for generate rule types
|
||||
func (p *ClusterPolicy) HasGenerate() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if rule.HasGenerate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
//HasVerifyImages checks for image verification rule types
|
||||
func (p *ClusterPolicy) HasVerifyImages() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
|
@ -78,6 +100,29 @@ func (r Rule) HasGenerate() bool {
|
|||
return !reflect.DeepEqual(r.Generation, Generation{})
|
||||
}
|
||||
|
||||
func (r Rule) MatchKinds() []string {
|
||||
matchKinds := r.MatchResources.ResourceDescription.Kinds
|
||||
for _, value := range r.MatchResources.All {
|
||||
matchKinds = append(matchKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
for _, value := range r.MatchResources.Any {
|
||||
matchKinds = append(matchKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
|
||||
return matchKinds
|
||||
}
|
||||
|
||||
func (r Rule) ExcludeKinds() []string {
|
||||
excludeKinds := r.ExcludeResources.ResourceDescription.Kinds
|
||||
for _, value := range r.ExcludeResources.All {
|
||||
excludeKinds = append(excludeKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
for _, value := range r.ExcludeResources.Any {
|
||||
excludeKinds = append(excludeKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
return excludeKinds
|
||||
}
|
||||
|
||||
// DeserializeAnyPattern deserialize apiextensions.JSON to []interface{}
|
||||
func (in *Validation) DeserializeAnyPattern() ([]interface{}, error) {
|
||||
if in.AnyPattern == nil {
|
||||
|
@ -122,6 +167,11 @@ func (in *Validation) DeepCopyInto(out *Validation) {
|
|||
*out = *in
|
||||
}
|
||||
}
|
||||
func (in *ForEachValidation) DeepCopyInto(out *ForEachValidation) {
|
||||
if out != nil {
|
||||
*out = *in
|
||||
}
|
||||
}
|
||||
func (gen *Generation) DeepCopyInto(out *Generation) {
|
||||
if out != nil {
|
||||
*out = *gen
|
||||
|
|
|
@ -43,6 +43,22 @@ func (in *APICall) DeepCopy() *APICall {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AdmissionRequestInfoObject) DeepCopyInto(out *AdmissionRequestInfoObject) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionRequestInfoObject.
|
||||
func (in *AdmissionRequestInfoObject) DeepCopy() *AdmissionRequestInfoObject {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AdmissionRequestInfoObject)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnyAllConditions) DeepCopyInto(out *AnyAllConditions) {
|
||||
*out = *in
|
||||
|
@ -244,6 +260,16 @@ func (in *ExcludeResources) DeepCopy() *ExcludeResources {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForEachValidation.
|
||||
func (in *ForEachValidation) DeepCopy() *ForEachValidation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ForEachValidation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenerateRequest) DeepCopyInto(out *GenerateRequest) {
|
||||
*out = *in
|
||||
|
@ -276,6 +302,7 @@ func (in *GenerateRequest) DeepCopyObject() runtime.Object {
|
|||
func (in *GenerateRequestContext) DeepCopyInto(out *GenerateRequestContext) {
|
||||
*out = *in
|
||||
in.UserRequestInfo.DeepCopyInto(&out.UserRequestInfo)
|
||||
out.AdmissionRequestInfo = in.AdmissionRequestInfo
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -144,8 +144,6 @@ func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapI
|
|||
|
||||
cd.restrictDevelopmentUsername = []string{"minikube-user", "kubernetes-admin"}
|
||||
|
||||
//TODO: this has been added to backward support command line arguments
|
||||
// will be removed in future and the configuration will be set only via configmaps
|
||||
if filterK8sResources != "" {
|
||||
cd.log.Info("init configuration from commandline arguments for filterK8sResources")
|
||||
cd.initFilters(filterK8sResources)
|
||||
|
@ -320,8 +318,6 @@ func (cd *ConfigData) load(cm v1.ConfigMap) (reconcilePolicyReport, updateWebhoo
|
|||
return
|
||||
}
|
||||
|
||||
//TODO: this has been added to backward support command line arguments
|
||||
// will be removed in future and the configuration will be set only via configmaps
|
||||
func (cd *ConfigData) initFilters(filters string) {
|
||||
logger := cd.log
|
||||
// parse and load the configuration
|
||||
|
@ -380,7 +376,6 @@ func parseKinds(list string) []k8Resource {
|
|||
element = strings.Trim(element, "[")
|
||||
element = strings.Trim(element, "]")
|
||||
elements := strings.Split(element, ",")
|
||||
//TODO: wildcards for namespace and name
|
||||
if len(elements) == 0 {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -76,8 +76,6 @@ func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dy
|
|||
}
|
||||
|
||||
//GetEventsInterface provides typed interface for events
|
||||
//TODO: can we use dynamic client to fetch the typed interface
|
||||
// or generate a kube client value to access the interface
|
||||
func (c *Client) GetEventsInterface() (event.EventInterface, error) {
|
||||
return c.kclient.CoreV1().Events(""), nil
|
||||
}
|
||||
|
|
|
@ -215,6 +215,7 @@ func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error {
|
|||
ctx.log.Error(err, "failed to marshal the UserInfo")
|
||||
return err
|
||||
}
|
||||
ctx.log.V(4).Info("Adding user info logs", "userRequestInfo", userRequestInfo)
|
||||
return ctx.AddJSON(objRaw)
|
||||
}
|
||||
|
||||
|
@ -263,7 +264,7 @@ func (ctx *Context) AddServiceAccount(userName string) error {
|
|||
if err := ctx.AddJSON(saNsRaw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.log.V(4).Info("Adding service account", "service account name", saName, "service account namespace", saNamespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -75,10 +75,10 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
|
|||
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name,
|
||||
"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
|
||||
|
||||
if err = MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err != nil {
|
||||
if err = MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, ""); err != nil {
|
||||
|
||||
// if the oldResource matched, return "false" to delete GR for it
|
||||
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err == nil {
|
||||
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, ""); err == nil {
|
||||
return &response.RuleResponse{
|
||||
Name: rule.Name,
|
||||
Type: "Generation",
|
||||
|
|
|
@ -52,7 +52,6 @@ func ProcessPatches(log logr.Logger, ruleName string, mutation kyverno.Mutation,
|
|||
continue
|
||||
}
|
||||
patchResource, err := applyPatch(resourceRaw, patchRaw)
|
||||
// TODO: continue on error if one of the patches fails, will add the failure event in such case
|
||||
if err != nil && patch.Operation == "remove" {
|
||||
log.Error(err, "failed to process JSON path or patch is a 'remove' operation")
|
||||
continue
|
||||
|
@ -79,7 +78,7 @@ func ProcessPatches(log logr.Logger, ruleName string, mutation kyverno.Mutation,
|
|||
}
|
||||
err = patchedResource.UnmarshalJSON(resourceRaw)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to unmmarshal resource")
|
||||
logger.Error(err, "failed to unmarshal resource")
|
||||
resp.Status = response.RuleStatusFail
|
||||
resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return resp, resource
|
||||
|
|
|
@ -65,7 +65,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
excludeResource = policyContext.ExcludeGroupRole
|
||||
}
|
||||
|
||||
if err = MatchesResourceDescription(patchedResource, rule, policyContext.AdmissionInfo, excludeResource, policyContext.NamespaceLabels); err != nil {
|
||||
if err = MatchesResourceDescription(patchedResource, rule, policyContext.AdmissionInfo, excludeResource, policyContext.NamespaceLabels, policyContext.Policy.Namespace); err != nil {
|
||||
logger.V(4).Info("rule not matched", "reason", err.Error())
|
||||
continue
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
if *ruleCopy, err = variables.SubstituteAllInRule(logger, ctx, *ruleCopy); err != nil {
|
||||
ruleResp := response.RuleResponse{
|
||||
Name: ruleCopy.Name,
|
||||
Type: utils.Validation.String(),
|
||||
Type: utils.Mutation.String(),
|
||||
Message: fmt.Sprintf("variable substitution failed: %s", err.Error()),
|
||||
Status: response.RuleStatusPass,
|
||||
}
|
||||
|
|
|
@ -256,13 +256,16 @@ func matchSubjects(ruleSubjects []rbacv1.Subject, userInfo authenticationv1.User
|
|||
}
|
||||
|
||||
//MatchesResourceDescription checks if the resource matches resource description of the rule or not
|
||||
func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef kyverno.Rule, admissionInfoRef kyverno.RequestInfo, dynamicConfig []string, namespaceLabels map[string]string) error {
|
||||
func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef kyverno.Rule, admissionInfoRef kyverno.RequestInfo, dynamicConfig []string, namespaceLabels map[string]string, policyNamespace string) error {
|
||||
|
||||
rule := ruleRef.DeepCopy()
|
||||
resource := *resourceRef.DeepCopy()
|
||||
admissionInfo := *admissionInfoRef.DeepCopy()
|
||||
|
||||
var reasonsForFailure []error
|
||||
if policyNamespace != "" && policyNamespace != resourceRef.GetNamespace() {
|
||||
return errors.New(" The policy and resource namespace are different. Therefore, policy skip this resource.")
|
||||
}
|
||||
if len(rule.MatchResources.Any) > 0 {
|
||||
// include object if ANY of the criteria match
|
||||
// so if one matches then break from loop
|
||||
|
|
|
@ -898,7 +898,7 @@ func TestMatchesResourceDescription(t *testing.T) {
|
|||
resource, _ := utils.ConvertToUnstructured(tc.Resource)
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil)
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil, "")
|
||||
if err != nil {
|
||||
if !tc.areErrorsExpected {
|
||||
t.Errorf("Testcase %d Unexpected error: %v", i+1, err)
|
||||
|
@ -966,7 +966,7 @@ func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
|
|||
}
|
||||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
|
||||
|
@ -1027,7 +1027,7 @@ func TestResourceDescriptionMatch_Name(t *testing.T) {
|
|||
}
|
||||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1087,7 +1087,7 @@ func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
|
|||
}
|
||||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1155,7 +1155,7 @@ func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
|
|||
}
|
||||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1224,7 +1224,7 @@ func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
|
|||
}
|
||||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1304,7 +1304,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
|
|||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription},
|
||||
ExcludeResources: kyverno.ExcludeResources{ResourceDescription: resourceDescriptionExclude}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err == nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err == nil {
|
||||
t.Errorf("Testcase has failed due to the following:\n Function has returned no error, even though it was supposed to fail")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,8 @@ func ValidateValueWithPattern(log logr.Logger, value, pattern interface{}) bool
|
|||
case nil:
|
||||
return validateValueWithNilPattern(log, value)
|
||||
case map[string]interface{}:
|
||||
// TODO: check if this is ever called?
|
||||
return validateValueWithMapPattern(log, value, typedPattern)
|
||||
case []interface{}:
|
||||
// TODO: check if this is ever called?
|
||||
log.Info("arrays are not supported as patterns")
|
||||
return false
|
||||
default:
|
||||
|
@ -57,7 +55,6 @@ func ValidateValueWithPattern(log logr.Logger, value, pattern interface{}) bool
|
|||
func validateValueWithMapPattern(log logr.Logger, value interface{}, typedPattern map[string]interface{}) bool {
|
||||
// verify the type of the resource value is map[string]interface,
|
||||
// we only check for existence of object, not the equality of content and value
|
||||
//TODO: check if adding
|
||||
_, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Info("Expected type map[string]interface{}", "type", fmt.Sprintf("%T", value), "value", value)
|
||||
|
|
|
@ -420,13 +420,13 @@ func isEmptyUnstructured(u *unstructured.Unstructured) bool {
|
|||
|
||||
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule
|
||||
func matches(logger logr.Logger, rule *kyverno.Rule, ctx *PolicyContext) bool {
|
||||
err := MatchesResourceDescription(ctx.NewResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels)
|
||||
err := MatchesResourceDescription(ctx.NewResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
|
||||
err := MatchesResourceDescription(ctx.OldResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels)
|
||||
err := MatchesResourceDescription(ctx.OldResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -188,7 +188,6 @@ func (gen *Generator) syncHandler(key Info) error {
|
|||
var err error
|
||||
switch key.Kind {
|
||||
case "ClusterPolicy":
|
||||
//TODO: policy is clustered resource so wont need namespace
|
||||
robj, err = gen.cpLister.Get(key.Name)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to get cluster policy", "name", key.Name)
|
||||
|
|
|
@ -64,7 +64,6 @@ type Controller struct {
|
|||
// dynamic shared informer factory
|
||||
dynamicInformer dynamicinformer.DynamicSharedInformerFactory
|
||||
|
||||
//TODO: list of generic informers
|
||||
// only support Namespaces for re-evaluation on resource updates
|
||||
nsInformer informers.GenericInformer
|
||||
log logr.Logger
|
||||
|
|
|
@ -286,7 +286,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
|
|||
return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err)
|
||||
}
|
||||
|
||||
_, info, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport, namespaceSelectorMap, stdin, rc)
|
||||
_, info, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport, namespaceSelectorMap, stdin, rc, true)
|
||||
if err != nil {
|
||||
return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
|
||||
}
|
||||
|
|
|
@ -524,16 +524,16 @@ func MutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
|
|||
|
||||
// ApplyPolicyOnResource - function to apply policy on resource
|
||||
func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured,
|
||||
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool, namespaceSelectorMap map[string]map[string]string, stdin bool, rc *ResultCounts) (*response.EngineResponse, policyreport.Info, error) {
|
||||
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool, namespaceSelectorMap map[string]map[string]string, stdin bool, rc *ResultCounts, printPatchResource bool) ([]*response.EngineResponse, policyreport.Info, error) {
|
||||
|
||||
var engineResponses []*response.EngineResponse
|
||||
namespaceLabels := make(map[string]string)
|
||||
operationIsDelete := false
|
||||
|
||||
if variables["request.operation"] == "DELETE" {
|
||||
operationIsDelete = true
|
||||
}
|
||||
|
||||
namespaceLabels := make(map[string]string)
|
||||
|
||||
policyWithNamespaceSelector := false
|
||||
for _, p := range policy.Spec.Rules {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
|
@ -547,7 +547,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
resourceNamespace := resource.GetNamespace()
|
||||
namespaceLabels = namespaceSelectorMap[resource.GetNamespace()]
|
||||
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
||||
return &response.EngineResponse{}, policyreport.Info{}, sanitizederror.NewWithError(fmt.Sprintf("failed to get namesapce labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil)
|
||||
return engineResponses, policyreport.Info{}, sanitizederror.NewWithError(fmt.Sprintf("failed to get namesapce labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,10 +575,14 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
}
|
||||
|
||||
mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx, NamespaceLabels: namespaceLabels})
|
||||
err = processMutateEngineResponse(policy, mutateResponse, resPath, rc, mutateLogPath, stdin, mutateLogPathIsDir, resource.GetName())
|
||||
if mutateResponse != nil {
|
||||
engineResponses = append(engineResponses, mutateResponse)
|
||||
}
|
||||
|
||||
err = processMutateEngineResponse(policy, mutateResponse, resPath, rc, mutateLogPath, stdin, mutateLogPathIsDir, resource.GetName(), printPatchResource)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return &response.EngineResponse{}, policyreport.Info{}, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
return engineResponses, policyreport.Info{}, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,6 +608,9 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
validateResponse = engine.Validate(policyCtx)
|
||||
info = ProcessValidateEngineResponse(policy, validateResponse, resPath, rc, policyReport)
|
||||
}
|
||||
if validateResponse != nil {
|
||||
engineResponses = append(engineResponses, validateResponse)
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
|
@ -624,10 +631,13 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
NamespaceLabels: namespaceLabels,
|
||||
}
|
||||
generateResponse := engine.Generate(policyContext)
|
||||
if generateResponse != nil {
|
||||
engineResponses = append(engineResponses, generateResponse)
|
||||
}
|
||||
processGenerateEngineResponse(policy, generateResponse, resPath, rc)
|
||||
}
|
||||
|
||||
return validateResponse, info, nil
|
||||
return engineResponses, info, nil
|
||||
}
|
||||
|
||||
// PrintMutatedOutput - function to print output in provided file or directory
|
||||
|
@ -892,7 +902,7 @@ func SetInStoreContext(mutatedPolicies []*v1.ClusterPolicy, variables map[string
|
|||
return variables
|
||||
}
|
||||
|
||||
func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string) error {
|
||||
func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string, printPatchResource bool) error {
|
||||
var policyHasMutate bool
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.HasMutate() {
|
||||
|
@ -929,7 +939,7 @@ func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *respo
|
|||
}
|
||||
}
|
||||
|
||||
if printMutatedRes {
|
||||
if printMutatedRes && printPatchResource {
|
||||
yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
|
||||
if err != nil {
|
||||
return sanitizederror.NewWithError("failed to marshal", err)
|
||||
|
@ -941,8 +951,7 @@ func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *respo
|
|||
if !stdin {
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath)
|
||||
}
|
||||
fmt.Printf("\n" + mutatedResource)
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("\n" + mutatedResource + "\n")
|
||||
}
|
||||
} else {
|
||||
err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated")
|
||||
|
@ -1009,3 +1018,34 @@ func GetKindsFromPolicy(policy *v1.ClusterPolicy) map[string]struct{} {
|
|||
}
|
||||
return kindOnwhichPolicyIsApplied
|
||||
}
|
||||
|
||||
//GetPatchedResourceFromPath - get patchedResource from given path
|
||||
func GetPatchedResourceFromPath(fs billy.Filesystem, path string, isGit bool, policyResourcePath string) (unstructured.Unstructured, error) {
|
||||
var patchedResourceBytes []byte
|
||||
var patchedResource unstructured.Unstructured
|
||||
var err error
|
||||
|
||||
if isGit {
|
||||
if len(path) > 0 {
|
||||
filep, err := fs.Open(filepath.Join(policyResourcePath, path))
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to open patchedResource file: %s. \nerror: %s", path, err)
|
||||
}
|
||||
patchedResourceBytes, err = ioutil.ReadAll(filep)
|
||||
}
|
||||
} else {
|
||||
patchedResourceBytes, err = getFileBytes(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("\n----------------------------------------------------------------------\nfailed to load patchedResource: %s. \nerror: %s\n----------------------------------------------------------------------\n", path, err)
|
||||
return patchedResource, err
|
||||
}
|
||||
|
||||
patchedResource, err = GetPatchedResource(patchedResourceBytes)
|
||||
if err != nil {
|
||||
return patchedResource, err
|
||||
}
|
||||
|
||||
return patchedResource, nil
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func Test_NamespaceSelector(t *testing.T) {
|
|||
for _, tc := range testcases {
|
||||
policyArray, _ := ut.GetPolicy(tc.policy)
|
||||
resourceArray, _ := GetResource(tc.resource)
|
||||
ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, false, tc.namespaceSelectorMap, false, rc)
|
||||
ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, false, tc.namespaceSelectorMap, false, rc, false)
|
||||
assert.Assert(t, int64(rc.Pass) == int64(tc.result.Pass))
|
||||
assert.Assert(t, int64(rc.Fail) == int64(tc.result.Fail))
|
||||
assert.Assert(t, int64(rc.Skip) == int64(tc.result.Skip))
|
||||
|
|
|
@ -275,6 +275,14 @@ func convertResourceToUnstructured(resourceYaml []byte) (*unstructured.Unstructu
|
|||
return resource, nil
|
||||
}
|
||||
|
||||
// GetPatchedResource converts raw bytes to unstructured object
|
||||
func GetPatchedResource(patchResourceBytes []byte) (patchedResource unstructured.Unstructured, err error) {
|
||||
getPatchedResource, err := GetResource(patchResourceBytes)
|
||||
patchedResource = *getPatchedResource[0]
|
||||
|
||||
return patchedResource, nil
|
||||
}
|
||||
|
||||
// getKindsFromPolicy will return the kinds from policy match block
|
||||
func getKindsFromPolicy(rule v1.Rule) map[string]bool {
|
||||
var resourceTypesMap = make(map[string]bool)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -14,11 +15,13 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kataras/tablewriter"
|
||||
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
|
||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
"github.com/kyverno/kyverno/pkg/generate"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
||||
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||
|
@ -30,17 +33,97 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
log "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
var longHelp = `
|
||||
Test command provides a facility to test policies on resources. User should provide the path of the folder containing test.yaml file.
|
||||
|
||||
kyverno test <path_to_folder_Contaning_test.yamls>
|
||||
or
|
||||
kyverno test <path_to_githubRepository>
|
||||
|
||||
The test.yaml have 4 parts:
|
||||
"policies" --> list of policies which are applied
|
||||
"resources" --> list of resources on which the policies are applied
|
||||
"variables" --> variable file path (this is an optinal parameter)
|
||||
"results" --> list of result expected on applying the policies on the resources
|
||||
`
|
||||
var exampleHelp = `
|
||||
test.yaml format:
|
||||
|
||||
For Validate Policy
|
||||
- name: test-1
|
||||
policies:
|
||||
- <path>
|
||||
- <path>
|
||||
resources:
|
||||
- <path>
|
||||
- <path>
|
||||
results:
|
||||
- policy: <name>
|
||||
rule: <name>
|
||||
resource: <name>
|
||||
namespace: <name> (OPTIONAL)
|
||||
kind: <name>
|
||||
result: <pass/fail/skip>
|
||||
|
||||
|
||||
For Mutate Policy
|
||||
|
||||
1) Policy (Namespaced-policy)
|
||||
- name: test-1
|
||||
policies:
|
||||
- <path>
|
||||
- <path>
|
||||
resources:
|
||||
- <path>
|
||||
- <path>
|
||||
results:
|
||||
- policy: <policy_namespace>/<policy_name>
|
||||
rule: <name>
|
||||
resource: <name>
|
||||
namespace: <name> (OPTIONAL)
|
||||
kind: <name>
|
||||
patchedResource: <path>
|
||||
result: <pass/fail/skip>
|
||||
|
||||
2) ClusterPolicy(Cluster-wide policy)
|
||||
- name: test-1
|
||||
policies:
|
||||
- <path>
|
||||
- <path>
|
||||
resources:
|
||||
- <path>
|
||||
- <path>
|
||||
results:
|
||||
- policy: <name>
|
||||
rule: <name>
|
||||
resource: <name>
|
||||
namespace: <name> (OPTIONAL)
|
||||
kind: <name>
|
||||
patchedResource: <path>
|
||||
result: <pass/fail/skip>
|
||||
|
||||
Result description:
|
||||
pass --> patched Resource generated from engine equals to patched Resource provided by the user.
|
||||
fail --> patched Resource generated from engine is not equals to patched provided by the user.
|
||||
skip --> rule is not applied.
|
||||
|
||||
For more visit --> https://kyverno.io/docs/kyverno-cli/#test
|
||||
`
|
||||
|
||||
// Command returns version command
|
||||
func Command() *cobra.Command {
|
||||
var cmd *cobra.Command
|
||||
var valuesFile, fileName string
|
||||
cmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "run tests from directory",
|
||||
Use: "test",
|
||||
Short: "run tests from directory",
|
||||
Long: longHelp,
|
||||
Example: exampleHelp,
|
||||
RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
@ -78,6 +161,9 @@ type TestResults struct {
|
|||
Result report.PolicyResult `json:"result"`
|
||||
Status report.PolicyResult `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Kind string `json:"kind"`
|
||||
Namespace string `json:"namespace"`
|
||||
PatchedResource string `json:"patchedResource"`
|
||||
AutoGeneratedRule string `json:"auto_generated_rule"`
|
||||
}
|
||||
|
||||
|
@ -239,13 +325,16 @@ func getLocalDirTestFiles(fs billy.Filesystem, path, fileName, valuesFile string
|
|||
return errors
|
||||
}
|
||||
|
||||
func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResults, infos []policyreport.Info) (map[string]report.PolicyReportResult, []TestResults) {
|
||||
func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResults, infos []policyreport.Info, policyResourcePath string, fs billy.Filesystem, isGit bool) (map[string]report.PolicyReportResult, []TestResults) {
|
||||
results := make(map[string]report.PolicyReportResult)
|
||||
now := metav1.Timestamp{Seconds: time.Now().Unix()}
|
||||
|
||||
for _, resp := range resps {
|
||||
policyName := resp.PolicyResponse.Policy.Name
|
||||
resourceName := resp.PolicyResponse.Resource.Name
|
||||
resourceKind := resp.PolicyResponse.Resource.Kind
|
||||
resourceNamespace := resp.PolicyResponse.Resource.Namespace
|
||||
policyNamespace := resp.PolicyResponse.Policy.Namespace
|
||||
|
||||
var rules []string
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
|
@ -262,27 +351,81 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
Message: buildMessage(resp),
|
||||
}
|
||||
|
||||
var patcheResourcePath []string
|
||||
for i, test := range testResults {
|
||||
var userDefinedPolicyNamespace string
|
||||
var userDefinedPolicyName string
|
||||
found, err := isNamespacedPolicy(test.Policy)
|
||||
if err != nil {
|
||||
log.Log.V(3).Info("error while checking the policy is namespaced or not", "policy: ", test.Policy, "error: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if found {
|
||||
userDefinedPolicyNamespace, userDefinedPolicyName = getUserDefinedPolicyNameAndNamespace(test.Policy)
|
||||
test.Policy = userDefinedPolicyName
|
||||
}
|
||||
|
||||
if test.Policy == policyName && test.Resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
if !util.ContainsString(rules, test.Rule) {
|
||||
|
||||
if !util.ContainsString(rules, "autogen-"+test.Rule) {
|
||||
if !util.ContainsString(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = report.StatusSkip
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = report.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
}
|
||||
|
||||
resultsKey := fmt.Sprintf("%s-%s-%s", test.Policy, test.Rule, test.Resource)
|
||||
patcheResourcePath = append(patcheResourcePath, test.PatchedResource)
|
||||
if _, ok := results[resultsKey]; !ok {
|
||||
results[resultsKey] = result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
if rule.Type != utils.Mutation.String() {
|
||||
continue
|
||||
}
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
|
||||
var result report.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name, resourceNamespace, resourceKind, resourceName)
|
||||
for _, resultK := range resultsKey {
|
||||
if val, ok := results[resultK]; ok {
|
||||
result = val
|
||||
resultKey = resultK
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
var x string
|
||||
for _, path := range patcheResourcePath {
|
||||
result.Result = report.StatusFail
|
||||
x = getAndComparePatchedResource(path, resp.PatchedResource, isGit, policyResourcePath, fs)
|
||||
if x == "pass" {
|
||||
result.Result = report.StatusPass
|
||||
break
|
||||
}
|
||||
}
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,18 +437,23 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
}
|
||||
|
||||
var result report.PolicyReportResult
|
||||
resultsKey := fmt.Sprintf("%s-%s-%s", info.PolicyName, rule.Name, infoResult.Resource.Name)
|
||||
if val, ok := results[resultsKey]; ok {
|
||||
result = val
|
||||
} else {
|
||||
continue
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
resultsKey = GetAllPossibleResultsKey("", info.PolicyName, rule.Name, infoResult.Resource.Namespace, infoResult.Resource.Kind, infoResult.Resource.Name)
|
||||
for _, resultK := range resultsKey {
|
||||
if val, ok := results[resultK]; ok {
|
||||
result = val
|
||||
resultKey = resultK
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
result.Rule = rule.Name
|
||||
result.Result = report.PolicyResult(rule.Status)
|
||||
result.Source = policyreport.SourceValue
|
||||
result.Timestamp = now
|
||||
results[resultsKey] = result
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,6 +461,64 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
return results, testResults
|
||||
}
|
||||
|
||||
func GetAllPossibleResultsKey(policyNs, policy, rule, resourceNsnamespace, kind, resource string) []string {
|
||||
var resultsKey []string
|
||||
resultKey1 := fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
resultKey2 := fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNsnamespace, kind, resource)
|
||||
resultKey3 := fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
|
||||
resultKey4 := fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, resourceNsnamespace, kind, resource)
|
||||
resultsKey = append(resultsKey, resultKey1, resultKey2, resultKey3, resultKey4)
|
||||
return resultsKey
|
||||
}
|
||||
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string) string {
|
||||
var resultKey string
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNs, kind, resource)
|
||||
}
|
||||
return resultKey
|
||||
}
|
||||
|
||||
func isNamespacedPolicy(policyNames string) (bool, error) {
|
||||
return regexp.MatchString("^[a-z]*/[a-z]*", policyNames)
|
||||
}
|
||||
|
||||
func getUserDefinedPolicyNameAndNamespace(policyName string) (string, string) {
|
||||
if strings.Contains(policyName, "/") {
|
||||
policy_n_ns := strings.Split(policyName, "/")
|
||||
namespace := policy_n_ns[0]
|
||||
policy := policy_n_ns[1]
|
||||
return namespace, policy
|
||||
}
|
||||
return "", policyName
|
||||
}
|
||||
|
||||
// getAndComparePatchedResource --> Get the patchedResource from the path provided by user
|
||||
// And compare this patchedResource with engine generated patcheResource.
|
||||
func getAndComparePatchedResource(path string, enginePatchedResource unstructured.Unstructured, isGit bool, policyResourcePath string, fs billy.Filesystem) string {
|
||||
var status string
|
||||
patchedResources, err := common.GetPatchedResourceFromPath(fs, path, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
var log logr.Logger
|
||||
matched, err := generate.ValidateResourceWithPattern(log, enginePatchedResource.UnstructuredContent(), patchedResources.UnstructuredContent())
|
||||
|
||||
if err != nil {
|
||||
status = "fail"
|
||||
}
|
||||
if matched == "" {
|
||||
status = "pass"
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func buildMessage(resp *response.EngineResponse) string {
|
||||
var bldr strings.Builder
|
||||
for _, ruleResp := range resp.PolicyResponse.Rules {
|
||||
|
@ -323,20 +529,22 @@ func buildMessage(resp *response.EngineResponse) string {
|
|||
return bldr.String()
|
||||
}
|
||||
|
||||
func getPolicyResourceFullPath(path []string, policyResourcePath string, isGit bool) []string {
|
||||
var pol []string
|
||||
func getFullPath(paths []string, policyResourcePath string, isGit bool) []string {
|
||||
var pols []string
|
||||
var pol string
|
||||
if !isGit {
|
||||
for _, p := range path {
|
||||
pol = append(pol, filepath.Join(policyResourcePath, p))
|
||||
for _, path := range paths {
|
||||
pol = filepath.Join(policyResourcePath, path)
|
||||
pols = append(pols, pol)
|
||||
}
|
||||
return pol
|
||||
return pols
|
||||
}
|
||||
return path
|
||||
return paths
|
||||
}
|
||||
|
||||
func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile string, isGit bool, policyResourcePath string, rc *resultCounts) (err error) {
|
||||
openAPIController, err := openapi.NewOpenAPIController()
|
||||
validateEngineResponses := make([]*response.EngineResponse, 0)
|
||||
engineResponses := make([]*response.EngineResponse, 0)
|
||||
var dClient *client.Client
|
||||
values := &Test{}
|
||||
var variablesString string
|
||||
|
@ -358,10 +566,16 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
|
|||
return err
|
||||
}
|
||||
|
||||
fullPolicyPath := getPolicyResourceFullPath(values.Policies, policyResourcePath, isGit)
|
||||
fullResourcePath := getPolicyResourceFullPath(values.Resources, policyResourcePath, isGit)
|
||||
policyFullPath := getFullPath(values.Policies, policyResourcePath, isGit)
|
||||
resourceFullPath := getFullPath(values.Resources, policyResourcePath, isGit)
|
||||
|
||||
policies, err := common.GetPoliciesFromPaths(fs, fullPolicyPath, isGit, policyResourcePath)
|
||||
for i, result := range values.Results {
|
||||
arrPatchedResource := []string{result.PatchedResource}
|
||||
patchedResourceFullPath := getFullPath(arrPatchedResource, policyResourcePath, isGit)
|
||||
values.Results[i].PatchedResource = patchedResourceFullPath[0]
|
||||
}
|
||||
|
||||
policies, err := common.GetPoliciesFromPaths(fs, policyFullPath, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -379,7 +593,7 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
|
|||
return sanitizederror.NewWithError("failed to print mutated policy", err)
|
||||
}
|
||||
|
||||
resources, err := common.GetResourceAccordingToResourcePath(fs, fullResourcePath, false, mutatedPolicies, dClient, "", false, isGit, policyResourcePath)
|
||||
resources, err := common.GetResourceAccordingToResourcePath(fs, resourceFullPath, false, mutatedPolicies, dClient, "", false, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -426,16 +640,15 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
|
|||
return sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err)
|
||||
}
|
||||
|
||||
validateErs, info, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true, namespaceSelectorMap, false, &resultCounts)
|
||||
ers, info, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true, namespaceSelectorMap, false, &resultCounts, false)
|
||||
if err != nil {
|
||||
return sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
|
||||
}
|
||||
validateEngineResponses = append(validateEngineResponses, validateErs)
|
||||
engineResponses = append(engineResponses, ers...)
|
||||
pvInfos = append(pvInfos, info)
|
||||
}
|
||||
}
|
||||
|
||||
resultsMap, testResults := buildPolicyResults(validateEngineResponses, values.Results, pvInfos)
|
||||
resultsMap, testResults := buildPolicyResults(engineResponses, values.Results, pvInfos, policyResourcePath, fs, isGit)
|
||||
resultErr := printTestResult(resultsMap, testResults, rc)
|
||||
if resultErr != nil {
|
||||
return sanitizederror.NewWithError("failed to print test result:", resultErr)
|
||||
|
@ -451,13 +664,19 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
boldRed := color.New(color.FgRed).Add(color.Bold)
|
||||
boldYellow := color.New(color.FgYellow).Add(color.Bold)
|
||||
boldFgCyan := color.New(color.FgCyan).Add(color.Bold)
|
||||
|
||||
for i, v := range testResults {
|
||||
res := new(Table)
|
||||
res.ID = i + 1
|
||||
res.Policy = boldFgCyan.Sprintf(v.Policy)
|
||||
res.Rule = boldFgCyan.Sprintf(v.Rule)
|
||||
res.Resource = boldFgCyan.Sprintf(v.Resource)
|
||||
|
||||
namespace := "default"
|
||||
if v.Namespace != "" {
|
||||
namespace = v.Namespace
|
||||
}
|
||||
|
||||
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
|
||||
var ruleNameInResultKey string
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
|
@ -465,7 +684,20 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
ruleNameInResultKey = v.Rule
|
||||
}
|
||||
|
||||
resultKey := fmt.Sprintf("%s-%s-%s", v.Policy, ruleNameInResultKey, v.Resource)
|
||||
resultKey := fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
found, _ := isNamespacedPolicy(v.Policy)
|
||||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else if found {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
res.Policy = boldFgCyan.Sprintf(ns) + "/" + boldFgCyan.Sprintf(v.Policy)
|
||||
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
|
||||
} else if v.Namespace != "" {
|
||||
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
}
|
||||
|
||||
var testRes report.PolicyReportResult
|
||||
if val, ok := resps[resultKey]; ok {
|
||||
|
@ -476,26 +708,28 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
table = append(table, res)
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Result == "" && v.Status != "" {
|
||||
v.Result = v.Status
|
||||
}
|
||||
|
||||
if testRes.Result == v.Result {
|
||||
res.Result = boldGreen.Sprintf("Pass")
|
||||
if testRes.Result == report.StatusSkip {
|
||||
res.Result = boldGreen.Sprintf("Pass")
|
||||
rc.Skip++
|
||||
} else {
|
||||
res.Result = boldGreen.Sprintf("Pass")
|
||||
rc.Pass++
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("test failed for policy=%s, rule=%s, resource=%s, expected=%s, received=%s \n",
|
||||
v.Policy, v.Rule, v.Resource, v.Result, testRes.Result)
|
||||
fmt.Printf("%s \n", testRes.Message)
|
||||
res.Result = boldRed.Sprintf("Fail")
|
||||
rc.Fail++
|
||||
}
|
||||
|
||||
table = append(table, res)
|
||||
}
|
||||
|
||||
printer.BorderTop, printer.BorderBottom, printer.BorderLeft, printer.BorderRight = true, true, true, true
|
||||
printer.CenterSeparator = "│"
|
||||
printer.ColumnSeparator = "│"
|
||||
|
@ -504,8 +738,10 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
printer.RowLengthTitle = func(rowsLength int) bool {
|
||||
return rowsLength > 10
|
||||
}
|
||||
|
||||
printer.HeaderBgColor = tablewriter.BgBlackColor
|
||||
printer.HeaderFgColor = tablewriter.FgGreenColor
|
||||
fmt.Printf("\n")
|
||||
printer.Print(table)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func NewPromConfig(metricsConfigData *config.MetricsConfigData, log logr.Logger)
|
|||
)
|
||||
|
||||
policyRuleInfoLabels := []string{
|
||||
"policy_validation_mode", "policy_type", "policy_background_mode", "policy_namespace", "policy_name", "rule_name", "rule_type",
|
||||
"policy_validation_mode", "policy_type", "policy_background_mode", "policy_namespace", "policy_name", "rule_name", "rule_type", "status_ready",
|
||||
}
|
||||
policyRuleInfoMetric := prom.NewGaugeVec(
|
||||
prom.GaugeOpts{
|
||||
|
|
|
@ -15,6 +15,7 @@ func (pc PromConfig) registerPolicyRuleInfoMetric(
|
|||
policyNamespace, policyName, ruleName string,
|
||||
ruleType metrics.RuleType,
|
||||
metricChangeType PolicyRuleInfoMetricChangeType,
|
||||
ready bool,
|
||||
) error {
|
||||
var metricValue float64
|
||||
switch metricChangeType {
|
||||
|
@ -40,6 +41,11 @@ func (pc PromConfig) registerPolicyRuleInfoMetric(
|
|||
policyNamespace = "-"
|
||||
}
|
||||
|
||||
status := "false"
|
||||
if ready {
|
||||
status = "true"
|
||||
}
|
||||
|
||||
pc.Metrics.PolicyRuleInfo.With(prom.Labels{
|
||||
"policy_validation_mode": string(policyValidationMode),
|
||||
"policy_type": string(policyType),
|
||||
|
@ -48,6 +54,7 @@ func (pc PromConfig) registerPolicyRuleInfoMetric(
|
|||
"policy_name": policyName,
|
||||
"rule_name": ruleName,
|
||||
"rule_type": string(ruleType),
|
||||
"status_ready": status,
|
||||
}).Set(metricValue)
|
||||
|
||||
return nil
|
||||
|
@ -64,12 +71,13 @@ func (pc PromConfig) AddPolicy(policy interface{}) error {
|
|||
policyType := metrics.Cluster
|
||||
policyNamespace := "" // doesn't matter for cluster policy
|
||||
policyName := inputPolicy.ObjectMeta.Name
|
||||
ready := inputPolicy.Status.Ready
|
||||
// registering the metrics on a per-rule basis
|
||||
for _, rule := range inputPolicy.Spec.Rules {
|
||||
ruleName := rule.Name
|
||||
ruleType := metrics.ParseRuleType(rule)
|
||||
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleCreated); err != nil {
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleCreated, ready); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -83,12 +91,13 @@ func (pc PromConfig) AddPolicy(policy interface{}) error {
|
|||
policyType := metrics.Namespaced
|
||||
policyNamespace := inputPolicy.ObjectMeta.Namespace
|
||||
policyName := inputPolicy.ObjectMeta.Name
|
||||
ready := inputPolicy.Status.Ready
|
||||
// registering the metrics on a per-rule basis
|
||||
for _, rule := range inputPolicy.Spec.Rules {
|
||||
ruleName := rule.Name
|
||||
ruleType := metrics.ParseRuleType(rule)
|
||||
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleCreated); err != nil {
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleCreated, ready); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -112,8 +121,9 @@ func (pc PromConfig) RemovePolicy(policy interface{}) error {
|
|||
policyName := inputPolicy.ObjectMeta.Name
|
||||
ruleName := rule.Name
|
||||
ruleType := metrics.ParseRuleType(rule)
|
||||
ready := inputPolicy.Status.Ready
|
||||
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleDeleted); err != nil {
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleDeleted, ready); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -130,8 +140,9 @@ func (pc PromConfig) RemovePolicy(policy interface{}) error {
|
|||
policyName := inputPolicy.ObjectMeta.Name
|
||||
ruleName := rule.Name
|
||||
ruleType := metrics.ParseRuleType(rule)
|
||||
ready := inputPolicy.Status.Ready
|
||||
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleDeleted); err != nil {
|
||||
if err = pc.registerPolicyRuleInfoMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, ruleName, ruleType, PolicyRuleDeleted, ready); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,23 +29,8 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.ClusterPoli
|
|||
continue
|
||||
}
|
||||
|
||||
match := rule.MatchResources
|
||||
exclude := rule.ExcludeResources
|
||||
|
||||
for _, value := range match.Any {
|
||||
pc.processExistingKinds(value.ResourceDescription.Kinds, policy, rule, logger)
|
||||
}
|
||||
for _, value := range match.All {
|
||||
pc.processExistingKinds(value.ResourceDescription.Kinds, policy, rule, logger)
|
||||
}
|
||||
for _, value := range exclude.All {
|
||||
pc.processExistingKinds(value.ResourceDescription.Kinds, policy, rule, logger)
|
||||
}
|
||||
for _, value := range exclude.Any {
|
||||
pc.processExistingKinds(value.ResourceDescription.Kinds, policy, rule, logger)
|
||||
}
|
||||
|
||||
pc.processExistingKinds(match.Kinds, policy, rule, logger)
|
||||
matchKinds := rule.MatchKinds()
|
||||
pc.processExistingKinds(matchKinds, policy, rule, logger)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +154,6 @@ type resourceManager interface {
|
|||
}
|
||||
|
||||
//Drop drop the cache after every rebuild interval mins
|
||||
//TODO: or drop based on the size
|
||||
func (rm *ResourceManager) Drop() {
|
||||
timeSince := time.Since(rm.time)
|
||||
if timeSince > time.Duration(rm.rebuildTime)*time.Second {
|
||||
|
|
126
pkg/policy/metrics.go
Normal file
126
pkg/policy/metrics.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package policy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
policyChangesMetric "github.com/kyverno/kyverno/pkg/metrics/policychanges"
|
||||
policyRuleInfoMetric "github.com/kyverno/kyverno/pkg/metrics/policyruleinfo"
|
||||
)
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricAddPolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricUpdatePolicy(logger logr.Logger, oldP, curP *kyverno.ClusterPolicy) {
|
||||
// removing the old rules associated metrics
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(oldP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// adding the new rules associated metrics
|
||||
err = policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(curP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricDeletePolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricAddPolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyCreated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricUpdatePolicy(logger logr.Logger, oldP, curP *kyverno.ClusterPolicy) {
|
||||
if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) {
|
||||
return
|
||||
}
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// curP will require a new kyverno_policy_changes_total metric if the above update involved change in the following fields:
|
||||
if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction {
|
||||
err = policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", curP.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricDeletePolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyDeleted)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricDeleteNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricAddNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyCreated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricUpdateNsPolicy(logger logr.Logger, oldP, curP *kyverno.Policy) {
|
||||
if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) {
|
||||
return
|
||||
}
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// curP will require a new kyverno_policy_changes_total metric if the above update involved change in the following fields:
|
||||
if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction {
|
||||
err = policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", curP.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricDeleteNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyDeleted)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricAddNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricUpdateNsPolicy(logger logr.Logger, oldP, curP *kyverno.Policy) {
|
||||
// removing the old rules associated metrics
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(oldP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// adding the new rules associated metrics
|
||||
err = policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(curP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/event"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
||||
"github.com/kyverno/kyverno/pkg/metrics"
|
||||
policyRuleInfoMetric "github.com/kyverno/kyverno/pkg/metrics/policyruleinfo"
|
||||
pm "github.com/kyverno/kyverno/pkg/policymutation"
|
||||
"github.com/kyverno/kyverno/pkg/policyreport"
|
||||
"github.com/kyverno/kyverno/pkg/resourcecache"
|
||||
|
@ -40,8 +39,6 @@ import (
|
|||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
|
||||
policyChangesMetric "github.com/kyverno/kyverno/pkg/metrics/policychanges"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -176,7 +173,6 @@ func NewPolicyController(
|
|||
|
||||
// resource manager
|
||||
// rebuild after 300 seconds/ 5 mins
|
||||
//TODO: pass the time in seconds instead of converting it internally
|
||||
pc.rm = NewResourceManager(30)
|
||||
|
||||
return &pc, nil
|
||||
|
@ -197,64 +193,6 @@ func (pc *PolicyController) canBackgroundProcess(p *kyverno.ClusterPolicy) bool
|
|||
return true
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricAddPolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricUpdatePolicy(logger logr.Logger, oldP, curP *kyverno.ClusterPolicy) {
|
||||
// removing the old rules associated metrics
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(oldP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// adding the new rules associated metrics
|
||||
err = policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(curP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricDeletePolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricAddPolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyCreated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricUpdatePolicy(logger logr.Logger, oldP, curP *kyverno.ClusterPolicy) {
|
||||
if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) {
|
||||
return
|
||||
}
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// curP will require a new kyverno_policy_changes_total metric if the above update involved change in the following fields:
|
||||
if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction {
|
||||
err = policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", curP.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricDeletePolicy(logger logr.Logger, p *kyverno.ClusterPolicy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyDeleted)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) addPolicy(obj interface{}) {
|
||||
logger := pc.log
|
||||
p := obj.(*kyverno.ClusterPolicy)
|
||||
|
@ -353,64 +291,6 @@ func (pc *PolicyController) deletePolicy(obj interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricAddNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricUpdateNsPolicy(logger logr.Logger, oldP, curP *kyverno.Policy) {
|
||||
// removing the old rules associated metrics
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(oldP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// adding the new rules associated metrics
|
||||
err = policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).AddPolicy(curP)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyRuleInfoMetricDeleteNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyRuleInfoMetric.ParsePromConfig(*pc.promConfig).RemovePolicy(p)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_rule_info_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricAddNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyCreated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's creation", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricUpdateNsPolicy(logger logr.Logger, oldP, curP *kyverno.Policy) {
|
||||
if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) {
|
||||
return
|
||||
}
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", oldP.Name)
|
||||
}
|
||||
// curP will require a new kyverno_policy_changes_total metric if the above update involved change in the following fields:
|
||||
if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction {
|
||||
err = policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's updation", "name", curP.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) registerPolicyChangesMetricDeleteNsPolicy(logger logr.Logger, p *kyverno.Policy) {
|
||||
err := policyChangesMetric.ParsePromConfig(*pc.promConfig).RegisterPolicy(p, policyChangesMetric.PolicyDeleted)
|
||||
if err != nil {
|
||||
logger.Error(err, "error occurred while registering kyverno_policy_changes_total metrics for the above policy's deletion", "name", p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) addNsPolicy(obj interface{}) {
|
||||
logger := pc.log
|
||||
p := obj.(*kyverno.Policy)
|
|
@ -10,13 +10,12 @@ import (
|
|||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
"github.com/jmespath/go-jmespath"
|
||||
c "github.com/kyverno/kyverno/pkg/common"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
comn "github.com/kyverno/kyverno/pkg/common"
|
||||
dclient "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
dclient "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
|
@ -177,12 +176,41 @@ func Validate(policy *kyverno.ClusterPolicy, client *dclient.Client, mock bool,
|
|||
}
|
||||
|
||||
// Validate Kind with match resource kinds
|
||||
for _, kind := range rule.MatchResources.Kinds {
|
||||
_, k := c.GetKindFromGVK(kind)
|
||||
if k == p.Kind {
|
||||
return fmt.Errorf("kind and match resource kind should not be the same.")
|
||||
match := rule.MatchResources
|
||||
exclude := rule.ExcludeResources
|
||||
for _, value := range match.Any {
|
||||
err := validateKinds(value.ResourceDescription.Kinds, mock, client, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the kind defined in the any match resource is invalid")
|
||||
}
|
||||
}
|
||||
for _, value := range match.All {
|
||||
err := validateKinds(value.ResourceDescription.Kinds, mock, client, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the kind defined in the all match resource is invalid")
|
||||
}
|
||||
}
|
||||
for _, value := range exclude.Any {
|
||||
err := validateKinds(value.ResourceDescription.Kinds, mock, client, p)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("the kind defined in the any exclude resource is invalid")
|
||||
}
|
||||
}
|
||||
for _, value := range exclude.All {
|
||||
err := validateKinds(value.ResourceDescription.Kinds, mock, client, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the kind defined in the all exclude resource is invalid")
|
||||
}
|
||||
}
|
||||
err := validateKinds(rule.MatchResources.Kinds, mock, client, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("match resource kind is invalid ")
|
||||
}
|
||||
err = validateKinds(rule.ExcludeResources.Kinds, mock, client, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("exclude resource kind is invalid ")
|
||||
}
|
||||
|
||||
// Validate string values in labels
|
||||
if !isLabelAndAnnotationsString(rule) {
|
||||
|
@ -1027,3 +1055,19 @@ func jsonPatchOnPod(rule kyverno.Rule) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func validateKinds(kinds []string, mock bool, client *dclient.Client, p kyverno.ClusterPolicy) error {
|
||||
for _, kind := range kinds {
|
||||
gv, k := comn.GetKindFromGVK(kind)
|
||||
if !mock {
|
||||
_, _, err := client.DiscoveryClient.FindResource(gv, k)
|
||||
if err != nil || strings.ToLower(k) == k {
|
||||
return fmt.Errorf("match resource kind %s is invalid ", k)
|
||||
}
|
||||
}
|
||||
if k == p.Kind {
|
||||
return fmt.Errorf("kind and match resource kind should not be the same")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ func GenerateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logg
|
|||
updateMsgs = append(updateMsgs, updateMsg)
|
||||
}
|
||||
|
||||
if patch, updateMsg := defaultFailurePolicy(policy, log); patch != nil {
|
||||
patches = append(patches, patch)
|
||||
updateMsgs = append(updateMsgs, updateMsg)
|
||||
}
|
||||
|
||||
patch, errs := GeneratePodControllerRule(*policy, log)
|
||||
if len(errs) > 0 {
|
||||
var errMsgs []string
|
||||
|
@ -307,6 +312,33 @@ func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy, log logr.Logg
|
|||
|
||||
return nil, ""
|
||||
}
|
||||
func defaultFailurePolicy(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
|
||||
// set failurePolicy to Fail if not present
|
||||
failurePolicy := string(kyverno.Fail)
|
||||
if policy.Spec.FailurePolicy == nil {
|
||||
log.V(4).Info("setting default value", "spec.failurePolicy", failurePolicy)
|
||||
jsonPatch := struct {
|
||||
Path string `json:"path"`
|
||||
Op string `json:"op"`
|
||||
Value string `json:"value"`
|
||||
}{
|
||||
"/spec/failurePolicy",
|
||||
"add",
|
||||
string(kyverno.Fail),
|
||||
}
|
||||
|
||||
patchByte, err := json.Marshal(jsonPatch)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to set default value", "spec.failurePolicy", failurePolicy)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
log.V(3).Info("generated JSON Patch to set default", "spec.failurePolicy", failurePolicy)
|
||||
return patchByte, fmt.Sprintf("default failurePolicy to '%s'", failurePolicy)
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// podControllersKey annotation could be:
|
||||
// scenario A: not exist, set default to "all", which generates on all pod controllers
|
||||
|
@ -534,20 +566,10 @@ func generateRuleForControllers(rule kyverno.Rule, controllers string, log logr.
|
|||
|
||||
match := rule.MatchResources
|
||||
exclude := rule.ExcludeResources
|
||||
matchResourceDescriptionsKinds := match.ResourceDescription.Kinds
|
||||
for _, value := range match.All {
|
||||
matchResourceDescriptionsKinds = append(matchResourceDescriptionsKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
for _, value := range match.Any {
|
||||
matchResourceDescriptionsKinds = append(matchResourceDescriptionsKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
excludeResourceDescriptionsKinds := exclude.ResourceDescription.Kinds
|
||||
for _, value := range exclude.All {
|
||||
excludeResourceDescriptionsKinds = append(excludeResourceDescriptionsKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
for _, value := range exclude.Any {
|
||||
excludeResourceDescriptionsKinds = append(excludeResourceDescriptionsKinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
|
||||
matchResourceDescriptionsKinds := rule.MatchKinds()
|
||||
excludeResourceDescriptionsKinds := rule.ExcludeKinds()
|
||||
|
||||
if !utils.ContainsString(matchResourceDescriptionsKinds, "Pod") ||
|
||||
(len(excludeResourceDescriptionsKinds) != 0 && !utils.ContainsString(excludeResourceDescriptionsKinds, "Pod")) {
|
||||
return kyvernoRule{}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package signal
|
||||
|
||||
//TODO: how to pick files based on OS compilation ?
|
||||
// import (
|
||||
// "os"
|
||||
// )
|
||||
|
||||
// var shutdownSignals = []os.Signal{os.Interrupt}
|
|
@ -421,7 +421,6 @@ func loadObjects(t *testing.T, path string) []k8sRuntime.Object {
|
|||
continue
|
||||
}
|
||||
t.Log(gvk)
|
||||
//TODO: add more details
|
||||
t.Logf("loaded object %s", gvk.Kind)
|
||||
resources = append(resources, obj)
|
||||
}
|
||||
|
|
|
@ -40,6 +40,12 @@ func GetPolicy(bytes []byte) (clusterPolicies []*v1.ClusterPolicy, err error) {
|
|||
return nil, fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
if policy.Namespace != "" || (policy.Namespace == "" && policy.Kind == "Policy") {
|
||||
if policy.Namespace == "" {
|
||||
policy.Namespace = "default"
|
||||
}
|
||||
policy.Kind = "ClusterPolicy"
|
||||
}
|
||||
clusterPolicies = append(clusterPolicies, policy)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
ktls "github.com/kyverno/kyverno/pkg/tls"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
informerv1 "k8s.io/client-go/informers/core/v1"
|
||||
|
@ -29,14 +28,14 @@ type Interface interface {
|
|||
GetTLSPemPair() (*ktls.PemPair, error)
|
||||
}
|
||||
type certManager struct {
|
||||
renewer *tls.CertRenewer
|
||||
renewer *ktls.CertRenewer
|
||||
secretInformer informerv1.SecretInformer
|
||||
secretQueue chan bool
|
||||
stopCh <-chan struct{}
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
func NewCertManager(secretInformer informerv1.SecretInformer, kubeClient kubernetes.Interface, certRenewer *tls.CertRenewer, log logr.Logger, stopCh <-chan struct{}) (Interface, error) {
|
||||
func NewCertManager(secretInformer informerv1.SecretInformer, kubeClient kubernetes.Interface, certRenewer *ktls.CertRenewer, log logr.Logger, stopCh <-chan struct{}) (Interface, error) {
|
||||
manager := &certManager{
|
||||
renewer: certRenewer,
|
||||
secretInformer: secretInformer,
|
||||
|
@ -59,7 +58,7 @@ func (m *certManager) addSecretFunc(obj interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
val, ok := secret.GetAnnotations()[tls.SelfSignedAnnotation]
|
||||
val, ok := secret.GetAnnotations()[ktls.SelfSignedAnnotation]
|
||||
if !ok || val != "true" {
|
||||
return
|
||||
}
|
||||
|
@ -74,7 +73,7 @@ func (m *certManager) updateSecretFunc(oldObj interface{}, newObj interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
val, ok := new.GetAnnotations()[tls.SelfSignedAnnotation]
|
||||
val, ok := new.GetAnnotations()[ktls.SelfSignedAnnotation]
|
||||
if !ok || val != "true" {
|
||||
return
|
||||
}
|
||||
|
@ -127,7 +126,7 @@ func (m *certManager) Run(stopCh <-chan struct{}) {
|
|||
})
|
||||
|
||||
m.log.Info("start managing certificate")
|
||||
certsRenewalTicker := time.NewTicker(tls.CertRenewalInterval)
|
||||
certsRenewalTicker := time.NewTicker(ktls.CertRenewalInterval)
|
||||
defer certsRenewalTicker.Stop()
|
||||
|
||||
for {
|
||||
|
@ -137,7 +136,7 @@ func (m *certManager) Run(stopCh <-chan struct{}) {
|
|||
if err != nil {
|
||||
m.log.Error(err, "failed to validate cert")
|
||||
|
||||
if !strings.Contains(err.Error(), tls.ErrorsNotFound) {
|
||||
if !strings.Contains(err.Error(), ktls.ErrorsNotFound) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +156,7 @@ func (m *certManager) Run(stopCh <-chan struct{}) {
|
|||
if err != nil {
|
||||
m.log.Error(err, "failed to validate cert")
|
||||
|
||||
if !strings.Contains(err.Error(), tls.ErrorsNotFound) {
|
||||
if !strings.Contains(err.Error(), ktls.ErrorsNotFound) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ package webhookconfig
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
admregapi "k8s.io/api/admissionregistration/v1"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -95,76 +96,66 @@ func (wrc *Register) GetKubePolicyDeployment() (*apps.Deployment, *unstructured.
|
|||
}
|
||||
|
||||
// debug mutating webhook
|
||||
func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
|
||||
func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, rule admregapi.Rule, operationTypes []admregapi.OperationType, failurePolicy admregapi.FailurePolicyType) admregapi.MutatingWebhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
reinvocationPolicy := admregapi.NeverReinvocationPolicy
|
||||
|
||||
return admregapi.MutatingWebhook{
|
||||
w := admregapi.MutatingWebhook{
|
||||
ReinvocationPolicy: &reinvocationPolicy,
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
URL: &url,
|
||||
CABundle: caData,
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rule, admregapi.Rule{}) {
|
||||
w.Rules = []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: rule,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
|
||||
func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, rule admregapi.Rule, operationTypes []admregapi.OperationType, failurePolicy admregapi.FailurePolicyType) admregapi.ValidatingWebhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
return admregapi.ValidatingWebhook{
|
||||
w := admregapi.ValidatingWebhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
URL: &url,
|
||||
CABundle: caData,
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rule, admregapi.Rule{}) {
|
||||
w.Rules = []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: rule,
|
||||
},
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// mutating webhook
|
||||
func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
|
||||
func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, rule admregapi.Rule, operationTypes []admregapi.OperationType, failurePolicy admregapi.FailurePolicyType) admregapi.MutatingWebhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
reinvocationPolicy := admregapi.NeverReinvocationPolicy
|
||||
reinvocationPolicy := admregapi.IfNeededReinvocationPolicy
|
||||
|
||||
return admregapi.MutatingWebhook{
|
||||
w := admregapi.MutatingWebhook{
|
||||
ReinvocationPolicy: &reinvocationPolicy,
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
|
@ -175,32 +166,27 @@ func generateMutatingWebhook(name, servicePath string, caData []byte, validation
|
|||
},
|
||||
CABundle: caData,
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rule, admregapi.Rule{}) {
|
||||
w.Rules = []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: rule,
|
||||
},
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// validating webhook
|
||||
func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
|
||||
func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, rule admregapi.Rule, operationTypes []admregapi.OperationType, failurePolicy admregapi.FailurePolicyType) admregapi.ValidatingWebhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
return admregapi.ValidatingWebhook{
|
||||
w := admregapi.ValidatingWebhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
Service: &admregapi.ServiceReference{
|
||||
|
@ -210,23 +196,19 @@ func generateValidatingWebhook(name, servicePath string, caData []byte, validati
|
|||
},
|
||||
CABundle: caData,
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
},
|
||||
SideEffects: &sideEffect,
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rule, admregapi.Rule{}) {
|
||||
w.Rules = []admregapi.RuleWithOperations{
|
||||
{
|
||||
Operations: operationTypes,
|
||||
Rule: rule,
|
||||
},
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
|
755
pkg/webhookconfig/configmanager.go
Normal file
755
pkg/webhookconfig/configmanager.go
Normal file
|
@ -0,0 +1,755 @@
|
|||
package webhookconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||
kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/resourcecache"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
admregapi "k8s.io/api/admissionregistration/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
var DefaultWebhookTimeout int64 = 10
|
||||
|
||||
// webhookConfigManager manges the webhook configuration dynamically
|
||||
// it is NOT multi-thread safe
|
||||
type webhookConfigManager struct {
|
||||
client *client.Client
|
||||
kyvernoClient *kyvernoclient.Clientset
|
||||
|
||||
pInformer kyvernoinformer.ClusterPolicyInformer
|
||||
npInformer kyvernoinformer.PolicyInformer
|
||||
|
||||
// pLister can list/get policy from the shared informer's store
|
||||
pLister kyvernolister.ClusterPolicyLister
|
||||
|
||||
// npLister can list/get namespace policy from the shared informer's store
|
||||
npLister kyvernolister.PolicyLister
|
||||
|
||||
// pListerSynced returns true if the cluster policy store has been synced at least once
|
||||
pListerSynced cache.InformerSynced
|
||||
|
||||
// npListerSynced returns true if the namespace policy store has been synced at least once
|
||||
npListerSynced cache.InformerSynced
|
||||
|
||||
resCache resourcecache.ResourceCache
|
||||
|
||||
mutateInformer cache.SharedIndexInformer
|
||||
validateInformer cache.SharedIndexInformer
|
||||
mutateInformerSynced cache.InformerSynced
|
||||
validateInformerSynced cache.InformerSynced
|
||||
|
||||
queue workqueue.RateLimitingInterface
|
||||
|
||||
// wildcardPolicy indicates the number of policies that matches all kinds (*) defined
|
||||
wildcardPolicy int64
|
||||
|
||||
createDefaultWebhook chan<- string
|
||||
|
||||
stopCh <-chan struct{}
|
||||
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
type manage interface {
|
||||
start()
|
||||
}
|
||||
|
||||
func newWebhookConfigManager(
|
||||
client *client.Client,
|
||||
kyvernoClient *kyvernoclient.Clientset,
|
||||
pInformer kyvernoinformer.ClusterPolicyInformer,
|
||||
npInformer kyvernoinformer.PolicyInformer,
|
||||
resCache resourcecache.ResourceCache,
|
||||
createDefaultWebhook chan<- string,
|
||||
stopCh <-chan struct{},
|
||||
log logr.Logger) manage {
|
||||
|
||||
m := &webhookConfigManager{
|
||||
client: client,
|
||||
kyvernoClient: kyvernoClient,
|
||||
pInformer: pInformer,
|
||||
npInformer: npInformer,
|
||||
resCache: resCache,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "configmanager"),
|
||||
wildcardPolicy: 0,
|
||||
createDefaultWebhook: createDefaultWebhook,
|
||||
stopCh: stopCh,
|
||||
log: log,
|
||||
}
|
||||
|
||||
m.pLister = pInformer.Lister()
|
||||
m.npLister = npInformer.Lister()
|
||||
|
||||
m.pListerSynced = pInformer.Informer().HasSynced
|
||||
m.npListerSynced = npInformer.Informer().HasSynced
|
||||
|
||||
mutateCache, _ := m.resCache.GetGVRCache(kindMutating)
|
||||
m.mutateInformer = mutateCache.GetInformer()
|
||||
m.mutateInformerSynced = mutateCache.GetInformer().HasSynced
|
||||
|
||||
validateCache, _ := m.resCache.GetGVRCache(kindValidating)
|
||||
m.validateInformer = validateCache.GetInformer()
|
||||
m.validateInformerSynced = validateCache.GetInformer().HasSynced
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) handleErr(err error, key interface{}) {
|
||||
logger := m.log
|
||||
if err == nil {
|
||||
m.queue.Forget(key)
|
||||
return
|
||||
}
|
||||
|
||||
if m.queue.NumRequeues(key) < 3 {
|
||||
logger.Error(err, "failed to sync policy", "key", key)
|
||||
m.queue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
|
||||
utilruntime.HandleError(err)
|
||||
logger.V(2).Info("dropping policy out of queue", "key", key)
|
||||
m.queue.Forget(key)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) addClusterPolicy(obj interface{}) {
|
||||
p := obj.(*kyverno.ClusterPolicy)
|
||||
if hasWildcard(p) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, int64(1))
|
||||
}
|
||||
m.enqueue(p)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) updateClusterPolicy(old, cur interface{}) {
|
||||
oldP := old.(*kyverno.ClusterPolicy)
|
||||
curP := cur.(*kyverno.ClusterPolicy)
|
||||
|
||||
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
|
||||
return
|
||||
}
|
||||
|
||||
if hasWildcard(oldP) && !hasWildcard(curP) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
|
||||
} else if !hasWildcard(oldP) && hasWildcard(curP) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, int64(1))
|
||||
}
|
||||
|
||||
m.enqueue(curP)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) deleteClusterPolicy(obj interface{}) {
|
||||
p, ok := obj.(*kyverno.ClusterPolicy)
|
||||
if !ok {
|
||||
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("error decoding object, invalid type"))
|
||||
return
|
||||
}
|
||||
p, ok = tombstone.Obj.(*kyverno.ClusterPolicy)
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
|
||||
return
|
||||
}
|
||||
m.log.V(4).Info("Recovered deleted ClusterPolicy '%s' from tombstone", "name", p.GetName())
|
||||
}
|
||||
|
||||
if hasWildcard(p) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
|
||||
}
|
||||
m.enqueue(p)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) addPolicy(obj interface{}) {
|
||||
p := obj.(*kyverno.Policy)
|
||||
if hasWildcard(p) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, int64(1))
|
||||
}
|
||||
|
||||
pol := kyverno.ClusterPolicy(*p)
|
||||
m.enqueue(&pol)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) updatePolicy(old, cur interface{}) {
|
||||
oldP := old.(*kyverno.Policy)
|
||||
curP := cur.(*kyverno.Policy)
|
||||
|
||||
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
|
||||
return
|
||||
}
|
||||
|
||||
if hasWildcard(oldP) && !hasWildcard(curP) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
|
||||
} else if !hasWildcard(oldP) && hasWildcard(curP) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, int64(1))
|
||||
}
|
||||
|
||||
pol := kyverno.ClusterPolicy(*curP)
|
||||
m.enqueue(&pol)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) deletePolicy(obj interface{}) {
|
||||
p, ok := obj.(*kyverno.Policy)
|
||||
if !ok {
|
||||
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("error decoding object, invalid type"))
|
||||
return
|
||||
}
|
||||
p, ok = tombstone.Obj.(*kyverno.Policy)
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
|
||||
return
|
||||
}
|
||||
m.log.V(4).Info("Recovered deleted ClusterPolicy '%s' from tombstone", "name", p.GetName())
|
||||
}
|
||||
|
||||
if hasWildcard(p) {
|
||||
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
|
||||
}
|
||||
|
||||
pol := kyverno.ClusterPolicy(*p)
|
||||
m.enqueue(&pol)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) deleteWebhook(obj interface{}) {
|
||||
m.log.WithName("deleteWebhook").Info("resource webhook configuration was deleted, recreating...")
|
||||
if webhook, ok := obj.(*unstructured.Unstructured); ok {
|
||||
k := webhook.GetKind()
|
||||
if (k == kindMutating && webhook.GetName() == config.MutatingWebhookConfigurationName) ||
|
||||
(k == kindValidating && webhook.GetName() == config.ValidatingWebhookConfigurationName) {
|
||||
m.enqueueAllPolicies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) enqueueAllPolicies() {
|
||||
logger := m.log.WithName("enqueueAllPolicies")
|
||||
|
||||
cpols, err := m.listPolicies("")
|
||||
if err != nil {
|
||||
logger.Error(err, "unabled to list clusterpolicies")
|
||||
}
|
||||
for _, cpol := range cpols {
|
||||
m.enqueue(cpol)
|
||||
logger.V(4).Info("added CLusterPolicy to the queue", "name", cpol.GetName())
|
||||
}
|
||||
|
||||
nsCache, ok := m.resCache.GetGVRCache("Namespace")
|
||||
if !ok {
|
||||
nsCache, err = m.resCache.CreateGVKInformer("Namespace")
|
||||
if err != nil {
|
||||
logger.Error(err, "unabled to create Namespace listser")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
namespaces, err := nsCache.Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
logger.Error(err, "unabled to list namespaces")
|
||||
return
|
||||
}
|
||||
|
||||
for _, ns := range namespaces {
|
||||
pols, err := m.listPolicies(ns.GetName())
|
||||
if err != nil {
|
||||
logger.Error(err, "unabled to list policies", "namespace", ns.GetName())
|
||||
}
|
||||
|
||||
for _, p := range pols {
|
||||
m.enqueue(p)
|
||||
logger.V(4).Info("added Policy to the queue", "namespace", p.GetName(), "name", p.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) enqueue(policy *kyverno.ClusterPolicy) {
|
||||
logger := m.log
|
||||
key, err := cache.MetaNamespaceKeyFunc(policy)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to enqueue policy")
|
||||
return
|
||||
}
|
||||
m.queue.Add(key)
|
||||
}
|
||||
|
||||
// start is a blocking call to configure webhook
|
||||
func (m *webhookConfigManager) start() {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer m.queue.ShutDown()
|
||||
|
||||
m.log.Info("starting")
|
||||
defer m.log.Info("shutting down")
|
||||
|
||||
if !cache.WaitForCacheSync(m.stopCh, m.pListerSynced, m.npListerSynced, m.mutateInformerSynced, m.validateInformerSynced) {
|
||||
m.log.Info("failed to sync informer cache")
|
||||
return
|
||||
}
|
||||
|
||||
m.pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: m.addClusterPolicy,
|
||||
UpdateFunc: m.updateClusterPolicy,
|
||||
DeleteFunc: m.deleteClusterPolicy,
|
||||
})
|
||||
|
||||
m.npInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: m.addPolicy,
|
||||
UpdateFunc: m.updatePolicy,
|
||||
DeleteFunc: m.deletePolicy,
|
||||
})
|
||||
|
||||
m.mutateInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
DeleteFunc: m.deleteWebhook,
|
||||
})
|
||||
|
||||
m.validateInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
DeleteFunc: m.deleteWebhook,
|
||||
})
|
||||
|
||||
for m.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) processNextWorkItem() bool {
|
||||
key, quit := m.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer m.queue.Done(key)
|
||||
err := m.sync(key.(string))
|
||||
m.handleErr(err, key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) sync(key string) error {
|
||||
logger := m.log.WithName("sync")
|
||||
startTime := time.Now()
|
||||
logger.V(4).Info("started syncing policy", "key", key, "startTime", startTime)
|
||||
defer func() {
|
||||
logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String())
|
||||
}()
|
||||
|
||||
namespace, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
logger.Info("invalid resource key", "key", key)
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.reconcileWebhook(namespace, name)
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) reconcileWebhook(namespace, name string) error {
|
||||
logger := m.log.WithName("reconcileWebhook").WithValues("namespace", namespace, "policy", name)
|
||||
|
||||
policy, err := m.getPolicy(namespace, name)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "unable to get policy object %s/%s", namespace, name)
|
||||
}
|
||||
|
||||
webhooks, err := m.buildWebhooks(namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.updateWebhookConfig(webhooks); err != nil {
|
||||
return errors.Wrapf(err, "failed to update webhook configurations for policy %s/%s", namespace, name)
|
||||
}
|
||||
|
||||
// DELETION of the policy
|
||||
if policy == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.updateStatus(policy); err != nil {
|
||||
return errors.Wrapf(err, "failed to update policy status %s/%s", namespace, name)
|
||||
}
|
||||
|
||||
logger.Info("policy is ready to serve admission requests")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) getPolicy(namespace, name string) (*kyverno.ClusterPolicy, error) {
|
||||
// TODO: test default/policy
|
||||
if namespace == "" {
|
||||
return m.pLister.Get(name)
|
||||
}
|
||||
|
||||
nsPolicy, err := m.npLister.Policies(namespace).Get(name)
|
||||
if err == nil && nsPolicy != nil {
|
||||
p := kyverno.ClusterPolicy(*nsPolicy)
|
||||
return &p, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) listPolicies(namespace string) ([]*kyverno.ClusterPolicy, error) {
|
||||
if namespace != "" {
|
||||
polList, err := m.npLister.Policies(namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list Policy")
|
||||
}
|
||||
|
||||
policies := make([]*kyverno.ClusterPolicy, len(polList))
|
||||
for i, pol := range polList {
|
||||
p := kyverno.ClusterPolicy(*pol)
|
||||
policies[i] = &p
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
cpolList, err := m.pLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list ClusterPolicy")
|
||||
}
|
||||
|
||||
return cpolList, nil
|
||||
}
|
||||
|
||||
const (
|
||||
apiGroups string = "apiGroups"
|
||||
apiVersions string = "apiVersions"
|
||||
resources string = "resources"
|
||||
)
|
||||
|
||||
// webhook is the instance that aggregates the GVK of existing policies
|
||||
// based on kind, failurePolicy and webhookTimeout
|
||||
type webhook struct {
|
||||
kind string
|
||||
maxWebhookTimeout int64
|
||||
failurePolicy kyverno.FailurePolicyType
|
||||
|
||||
// rule represents the same rule struct of the webhook using a map object
|
||||
// https://github.com/kubernetes/api/blob/master/admissionregistration/v1/types.go#L25
|
||||
rule map[string]interface{}
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook, err error) {
|
||||
mutateIgnore := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
|
||||
mutateFail := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Fail)
|
||||
validateIgnore := newWebhook(kindValidating, DefaultWebhookTimeout, kyverno.Ignore)
|
||||
validateFail := newWebhook(kindValidating, DefaultWebhookTimeout, kyverno.Fail)
|
||||
|
||||
if atomic.LoadInt64(&m.wildcardPolicy) != 0 {
|
||||
for _, w := range []*webhook{mutateIgnore, mutateFail, validateIgnore, validateFail} {
|
||||
setWildcardConfig(w)
|
||||
}
|
||||
|
||||
m.log.V(4).WithName("buildWebhooks").Info("warning: found wildcard policy, setting webhook configurations to accept admission requests of all kinds")
|
||||
return append(res, mutateIgnore, mutateFail, validateIgnore, validateFail), nil
|
||||
}
|
||||
|
||||
policies, err := m.listPolicies(namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list current policies")
|
||||
}
|
||||
|
||||
for _, p := range policies {
|
||||
if p.HasValidate() || p.HasGenerate() {
|
||||
if p.Spec.FailurePolicy != nil && *p.Spec.FailurePolicy == kyverno.Ignore {
|
||||
m.mergeWebhook(validateIgnore, p)
|
||||
} else {
|
||||
m.mergeWebhook(validateFail, p)
|
||||
}
|
||||
}
|
||||
|
||||
if p.HasMutate() || p.HasGenerate() {
|
||||
if p.Spec.FailurePolicy != nil && *p.Spec.FailurePolicy == kyverno.Ignore {
|
||||
m.mergeWebhook(mutateIgnore, p)
|
||||
} else {
|
||||
m.mergeWebhook(mutateFail, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, mutateIgnore, mutateFail, validateIgnore, validateFail)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) updateWebhookConfig(webhooks []*webhook) error {
|
||||
logger := m.log.WithName("updateWebhookConfig")
|
||||
webhooksMap := make(map[string]interface{}, len(webhooks))
|
||||
for _, w := range webhooks {
|
||||
key := webhookKey(w.kind, string(w.failurePolicy))
|
||||
webhooksMap[key] = w
|
||||
}
|
||||
|
||||
var errs []string
|
||||
if err := m.compareAndUpdateWebhook(kindMutating, getResourceMutatingWebhookConfigName(""), webhooksMap); err != nil {
|
||||
logger.V(4).Info("failed to update mutatingwebhookconfigurations", "error", err.Error())
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
|
||||
if err := m.compareAndUpdateWebhook(kindValidating, getResourceValidatingWebhookConfigName(""), webhooksMap); err != nil {
|
||||
logger.V(4).Info("failed to update validatingwebhookconfigurations", "error", err.Error())
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) getWebhook(webhookKind, webhookName string) (resourceWebhook *unstructured.Unstructured, err error) {
|
||||
get := func() error {
|
||||
webhookCache, _ := m.resCache.GetGVRCache(webhookKind)
|
||||
|
||||
resourceWebhook, err = webhookCache.Lister().Get(webhookName)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "unable to get %s/%s", webhookKind, webhookName)
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
m.createDefaultWebhook <- webhookKind
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
retryGetWebhook := common.RetryFunc(time.Second, 10*time.Second, get, m.log)
|
||||
if err := retryGetWebhook(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resourceWebhook, nil
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) compareAndUpdateWebhook(webhookKind, webhookName string, webhooksMap map[string]interface{}) error {
|
||||
logger := m.log.WithName("compareAndUpdateWebhook").WithValues("kind", webhookKind, "name", webhookName)
|
||||
resourceWebhook, err := m.getWebhook(webhookKind, webhookName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
webhooksUntyped, _, err := unstructured.NestedSlice(resourceWebhook.UnstructuredContent(), "webhooks")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to fetch tag webhooks for %s/%s", webhookKind, webhookName)
|
||||
}
|
||||
|
||||
newWebooks := make([]interface{}, len(webhooksUntyped))
|
||||
copy(newWebooks, webhooksUntyped)
|
||||
var changed bool
|
||||
for i, webhookUntyed := range webhooksUntyped {
|
||||
existingWebhook, ok := webhookUntyed.(map[string]interface{})
|
||||
if !ok {
|
||||
logger.Error(errors.New("type mismatched"), "expected map[string]interface{}, got %T", webhooksUntyped)
|
||||
continue
|
||||
}
|
||||
|
||||
failurePolicy, _, err := unstructured.NestedString(existingWebhook, "failurePolicy")
|
||||
if err != nil {
|
||||
logger.Error(errors.New("type mismatched"), "expected string, got %T", failurePolicy)
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
rules, _, err := unstructured.NestedSlice(existingWebhook, "rules")
|
||||
if err != nil {
|
||||
logger.Error(err, "type mismatched, expected []interface{}, got %T", rules)
|
||||
continue
|
||||
}
|
||||
|
||||
newWebhook := webhooksMap[webhookKey(webhookKind, failurePolicy)]
|
||||
w, ok := newWebhook.(*webhook)
|
||||
if !ok {
|
||||
logger.Error(errors.New("type mismatched"), "expected *webhook, got %T", newWebooks)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rules, []interface{}{w.rule}) {
|
||||
changed = true
|
||||
|
||||
tmpRules, ok := newWebooks[i].(map[string]interface{})["rules"].([]interface{})
|
||||
if !ok {
|
||||
// init operations
|
||||
ops := []string{string(admregapi.Create), string(admregapi.Update), string(admregapi.Delete), string(admregapi.Connect)}
|
||||
if webhookKind == kindMutating {
|
||||
ops = []string{string(admregapi.Create), string(admregapi.Update)}
|
||||
}
|
||||
|
||||
tmpRules = []interface{}{map[string]interface{}{}}
|
||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), ops, "operations"); err != nil {
|
||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiGroups)
|
||||
}
|
||||
}
|
||||
|
||||
if w.rule == nil || reflect.DeepEqual(w.rule, map[string]interface{}{}) {
|
||||
// zero kyverno policy with the current failurePolicy, reset webhook rules to empty
|
||||
newWebooks[i].(map[string]interface{})["rules"] = []interface{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[apiGroups].([]string), apiGroups); err != nil {
|
||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiGroups)
|
||||
}
|
||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[apiVersions].([]string), apiVersions); err != nil {
|
||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiVersions)
|
||||
}
|
||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[resources].([]string), resources); err != nil {
|
||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, resources)
|
||||
}
|
||||
|
||||
newWebooks[i].(map[string]interface{})["rules"] = tmpRules
|
||||
}
|
||||
|
||||
if err = unstructured.SetNestedField(newWebooks[i].(map[string]interface{}), w.maxWebhookTimeout, "timeoutSeconds"); err != nil {
|
||||
return errors.Wrapf(err, "unable to set webhooks[%d].timeoutSeconds to %v", i, w.maxWebhookTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
logger.V(4).Info("webhook configuration has been changed, updating")
|
||||
if err := unstructured.SetNestedSlice(resourceWebhook.UnstructuredContent(), newWebooks, "webhooks"); err != nil {
|
||||
return errors.Wrap(err, "unable to set new webhooks")
|
||||
}
|
||||
|
||||
if _, err := m.client.UpdateResource(resourceWebhook.GetAPIVersion(), resourceWebhook.GetKind(), "", resourceWebhook, false); err != nil {
|
||||
return errors.Wrapf(err, "unable to update %s/%s: %s", resourceWebhook.GetAPIVersion(), resourceWebhook.GetKind(), resourceWebhook.GetName())
|
||||
}
|
||||
|
||||
logger.V(4).Info("successfully updated the webhook configuration")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *webhookConfigManager) updateStatus(policy *kyverno.ClusterPolicy) error {
|
||||
policyCopy := policy.DeepCopy()
|
||||
policyCopy.Status.Ready = true
|
||||
if policy.GetNamespace() == "" {
|
||||
_, err := m.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), policyCopy, v1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := m.kyvernoClient.KyvernoV1().Policies(policyCopy.GetNamespace()).UpdateStatus(context.TODO(), (*kyverno.Policy)(policyCopy), v1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// mergeWebhook merges the matching kinds of the policy to webhook.rule
|
||||
func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy *kyverno.ClusterPolicy) {
|
||||
matchedGVK := make([]string, 0)
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
matchedGVK = append(matchedGVK, rule.MatchKinds()...)
|
||||
if rule.HasGenerate() {
|
||||
matchedGVK = append(matchedGVK, rule.Generation.ResourceSpec.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
gvkMap := make(map[string]int)
|
||||
gvrList := make([]schema.GroupVersionResource, 0)
|
||||
for _, gvk := range matchedGVK {
|
||||
if _, ok := gvkMap[gvk]; !ok {
|
||||
gvkMap[gvk] = 1
|
||||
|
||||
// note: webhook stores GVR in its rules while policy stores GVK in its rules definition
|
||||
gv, k := common.GetKindFromGVK(gvk)
|
||||
_, gvr, err := m.client.DiscoveryClient.FindResource(gv, k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
gvrList = append(gvrList, gvr)
|
||||
}
|
||||
}
|
||||
|
||||
var groups, versions, rsrcs []string
|
||||
if val, ok := dst.rule[apiGroups]; ok {
|
||||
groups = make([]string, len(val.([]string)))
|
||||
copy(groups, val.([]string))
|
||||
}
|
||||
|
||||
if val, ok := dst.rule[apiVersions]; ok {
|
||||
versions = make([]string, len(val.([]string)))
|
||||
copy(versions, val.([]string))
|
||||
}
|
||||
if val, ok := dst.rule[resources]; ok {
|
||||
rsrcs = make([]string, len(val.([]string)))
|
||||
copy(rsrcs, val.([]string))
|
||||
}
|
||||
|
||||
for _, gvr := range gvrList {
|
||||
groups = append(groups, gvr.Group)
|
||||
versions = append(versions, gvr.Version)
|
||||
rsrcs = append(rsrcs, gvr.Resource)
|
||||
}
|
||||
|
||||
dst.rule[apiGroups] = removeDuplicates(groups)
|
||||
dst.rule[apiVersions] = removeDuplicates(versions)
|
||||
dst.rule[resources] = removeDuplicates(rsrcs)
|
||||
|
||||
if policy.Spec.WebhookTimeoutSeconds != nil {
|
||||
if dst.maxWebhookTimeout < int64(*policy.Spec.WebhookTimeoutSeconds) {
|
||||
dst.maxWebhookTimeout = int64(*policy.Spec.WebhookTimeoutSeconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDuplicates(items []string) (res []string) {
|
||||
set := make(map[string]int)
|
||||
for _, item := range items {
|
||||
if _, ok := set[item]; !ok {
|
||||
set[item] = 1
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newWebhook(kind string, timeout int64, failurePolicy kyverno.FailurePolicyType) *webhook {
|
||||
return &webhook{
|
||||
kind: kind,
|
||||
maxWebhookTimeout: timeout,
|
||||
failurePolicy: failurePolicy,
|
||||
rule: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func webhookKey(webhookKind, failurePolicy string) string {
|
||||
return strings.Join([]string{webhookKind, failurePolicy}, "/")
|
||||
}
|
||||
|
||||
func hasWildcard(policy interface{}) bool {
|
||||
if p, ok := policy.(*kyverno.ClusterPolicy); ok {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if kinds := rule.MatchKinds(); utils.ContainsString(kinds, "*") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p, ok := policy.(*kyverno.Policy); ok {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if kinds := rule.MatchKinds(); utils.ContainsString(kinds, "*") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setWildcardConfig(w *webhook) {
|
||||
w.rule[apiGroups] = []string{"*"}
|
||||
w.rule[apiVersions] = []string{"*"}
|
||||
w.rule[resources] = []string{"*/*"}
|
||||
}
|
|
@ -74,7 +74,7 @@ func (t *Monitor) SetTime(tm time.Time) {
|
|||
|
||||
// Run runs the checker and verify the resource update
|
||||
func (t *Monitor) Run(register *Register, certRenewer *tls.CertRenewer, eventGen event.Interface, stopCh <-chan struct{}) {
|
||||
logger := t.log
|
||||
logger := t.log.WithName("webhookMonitor")
|
||||
|
||||
logger.V(4).Info("starting webhook monitor", "interval", idleCheckInterval.String())
|
||||
status := newStatusControl(register, eventGen, t.log.WithName("WebhookStatusControl"))
|
||||
|
@ -82,8 +82,23 @@ func (t *Monitor) Run(register *Register, certRenewer *tls.CertRenewer, eventGen
|
|||
ticker := time.NewTicker(tickerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
createDefaultWebhook := register.createDefaultWebhook
|
||||
for {
|
||||
select {
|
||||
case webhookKind := <-createDefaultWebhook:
|
||||
logger.Info("received recreation request for resource webhook")
|
||||
if webhookKind == kindMutating {
|
||||
err := register.createResourceMutatingWebhookConfiguration(register.readCaData())
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to create default MutatingWebhookConfiguration for resources, the webhook will be reconciled", "interval", tickerInterval)
|
||||
}
|
||||
} else if webhookKind == kindValidating {
|
||||
err := register.createResourceValidatingWebhookConfiguration(register.readCaData())
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to create default ValidatingWebhookConfiguration for resources, the webhook will be reconciled", "interval", tickerInterval)
|
||||
}
|
||||
}
|
||||
|
||||
case <-ticker.C:
|
||||
|
||||
err := registerWebhookIfNotPresent(register, t.log.WithName("registerWebhookIfNotPresent"))
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
admregapi "k8s.io/api/admissionregistration/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (wrc *Register) contructPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
func (wrc *Register) constructPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
|
@ -24,16 +24,19 @@ func (wrc *Register) contructPolicyValidatingWebhookConfig(caData []byte) *admre
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"clusterpolicies/*", "policies/*"},
|
||||
"kyverno.io",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"clusterpolicies/*", "policies/*"},
|
||||
APIGroups: []string{"kyverno.io"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *Register) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
func (wrc *Register) constructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
logger := wrc.log
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath)
|
||||
logger.V(4).Info("Debug PolicyValidatingWebhookConfig is registered with url ", "url", url)
|
||||
|
@ -49,16 +52,19 @@ func (wrc *Register) contructDebugPolicyValidatingWebhookConfig(caData []byte) *
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"clusterpolicies/*", "policies/*"},
|
||||
"kyverno.io",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"clusterpolicies/*", "policies/*"},
|
||||
APIGroups: []string{"kyverno.io"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *Register) contructPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
func (wrc *Register) constructPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyMutatingWebhookConfigurationName,
|
||||
|
@ -73,16 +79,19 @@ func (wrc *Register) contructPolicyMutatingWebhookConfig(caData []byte) *admrega
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"clusterpolicies/*", "policies/*"},
|
||||
"kyverno.io",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"clusterpolicies/*", "policies/*"},
|
||||
APIGroups: []string{"kyverno.io"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *Register) contructDebugPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
func (wrc *Register) constructDebugPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
logger := wrc.log
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyMutatingWebhookServicePath)
|
||||
logger.V(4).Info("Debug PolicyMutatingWebhookConfig is registered with url ", "url", url)
|
||||
|
@ -98,10 +107,13 @@ func (wrc *Register) contructDebugPolicyMutatingWebhookConfig(caData []byte) *ad
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"clusterpolicies/*", "policies/*"},
|
||||
"kyverno.io",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"clusterpolicies/*", "policies/*"},
|
||||
APIGroups: []string{"kyverno.io"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -8,12 +8,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/resourcecache"
|
||||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
"github.com/pkg/errors"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
admregapi "k8s.io/api/admissionregistration/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -34,36 +36,54 @@ const (
|
|||
// 4. Resource Mutation
|
||||
// 5. Webhook Status Mutation
|
||||
type Register struct {
|
||||
client *client.Client
|
||||
clientConfig *rest.Config
|
||||
resCache resourcecache.ResourceCache
|
||||
serverIP string // when running outside a cluster
|
||||
timeoutSeconds int32
|
||||
log logr.Logger
|
||||
debug bool
|
||||
client *client.Client
|
||||
clientConfig *rest.Config
|
||||
resCache resourcecache.ResourceCache
|
||||
serverIP string // when running outside a cluster
|
||||
timeoutSeconds int32
|
||||
log logr.Logger
|
||||
debug bool
|
||||
autoUpdateWebhooks bool
|
||||
|
||||
UpdateWebhookChan chan bool
|
||||
UpdateWebhookChan chan bool
|
||||
createDefaultWebhook chan string
|
||||
|
||||
// manage implements methods to manage webhook configurations
|
||||
manage
|
||||
}
|
||||
|
||||
// NewRegister creates new Register instance
|
||||
func NewRegister(
|
||||
clientConfig *rest.Config,
|
||||
client *client.Client,
|
||||
kyvernoClient *kyvernoclient.Clientset,
|
||||
resCache resourcecache.ResourceCache,
|
||||
pInformer kyvernoinformer.ClusterPolicyInformer,
|
||||
npInformer kyvernoinformer.PolicyInformer,
|
||||
serverIP string,
|
||||
webhookTimeout int32,
|
||||
debug bool,
|
||||
autoUpdateWebhooks bool,
|
||||
stopCh <-chan struct{},
|
||||
log logr.Logger) *Register {
|
||||
return &Register{
|
||||
clientConfig: clientConfig,
|
||||
client: client,
|
||||
resCache: resCache,
|
||||
serverIP: serverIP,
|
||||
timeoutSeconds: webhookTimeout,
|
||||
log: log.WithName("Register"),
|
||||
debug: debug,
|
||||
UpdateWebhookChan: make(chan bool),
|
||||
register := &Register{
|
||||
clientConfig: clientConfig,
|
||||
client: client,
|
||||
resCache: resCache,
|
||||
serverIP: serverIP,
|
||||
timeoutSeconds: webhookTimeout,
|
||||
log: log.WithName("Register"),
|
||||
debug: debug,
|
||||
autoUpdateWebhooks: autoUpdateWebhooks,
|
||||
UpdateWebhookChan: make(chan bool),
|
||||
createDefaultWebhook: make(chan string),
|
||||
}
|
||||
|
||||
if register.autoUpdateWebhooks {
|
||||
register.manage = newWebhookConfigManager(client, kyvernoClient, pInformer, npInformer, resCache, register.createDefaultWebhook, stopCh, log.WithName("WebhookConfigManager"))
|
||||
}
|
||||
|
||||
return register
|
||||
}
|
||||
|
||||
// Register clean up the old webhooks and re-creates admission webhooks configs on cluster
|
||||
|
@ -109,6 +129,9 @@ func (wrc *Register) Register() error {
|
|||
return fmt.Errorf("%s", strings.Join(errors, ","))
|
||||
}
|
||||
|
||||
if wrc.autoUpdateWebhooks {
|
||||
go wrc.manage.start()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -121,19 +144,19 @@ func (wrc *Register) Check() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := mutatingCache.Lister().Get(wrc.getResourceMutatingWebhookConfigName()); err != nil {
|
||||
if _, err := mutatingCache.Lister().Get(getResourceMutatingWebhookConfigName(wrc.serverIP)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := validatingCache.Lister().Get(wrc.getResourceValidatingWebhookConfigName()); err != nil {
|
||||
if _, err := validatingCache.Lister().Get(getResourceValidatingWebhookConfigName(wrc.serverIP)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := mutatingCache.Lister().Get(wrc.getPolicyMutatingWebhookConfigurationName()); err != nil {
|
||||
if _, err := mutatingCache.Lister().Get(getPolicyMutatingWebhookConfigurationName(wrc.serverIP)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := validatingCache.Lister().Get(wrc.getPolicyValidatingWebhookConfigurationName()); err != nil {
|
||||
if _, err := validatingCache.Lister().Get(getPolicyValidatingWebhookConfigurationName(wrc.serverIP)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -151,10 +174,11 @@ func (wrc *Register) Remove(cleanUp chan<- struct{}) {
|
|||
wrc.removeSecrets()
|
||||
}
|
||||
|
||||
// +deprecated
|
||||
// UpdateWebhookConfigurations updates resource webhook configurations dynamically
|
||||
// base on the UPDATEs of Kyverno init-config ConfigMap
|
||||
//
|
||||
// it currently updates namespaceSelector only, can be extend to update other fieids
|
||||
// it currently updates namespaceSelector only, can be extend to update other fields
|
||||
func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Interface) {
|
||||
logger := wrc.log.WithName("UpdateWebhookConfigurations")
|
||||
for {
|
||||
|
@ -178,17 +202,17 @@ func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Interface)
|
|||
}
|
||||
|
||||
if err := wrc.updateResourceMutatingWebhookConfiguration(nsSelector); err != nil {
|
||||
logger.Error(err, "unable to update mutatingWebhookConfigurations", "name", wrc.getResourceMutatingWebhookConfigName())
|
||||
logger.Error(err, "unable to update mutatingWebhookConfigurations", "name", getResourceMutatingWebhookConfigName(wrc.serverIP))
|
||||
go func() { wrc.UpdateWebhookChan <- true }()
|
||||
} else {
|
||||
logger.Info("successfully updated mutatingWebhookConfigurations", "name", wrc.getResourceMutatingWebhookConfigName())
|
||||
logger.Info("successfully updated mutatingWebhookConfigurations", "name", getResourceMutatingWebhookConfigName(wrc.serverIP))
|
||||
}
|
||||
|
||||
if err := wrc.updateResourceValidatingWebhookConfiguration(nsSelector); err != nil {
|
||||
logger.Error(err, "unable to update validatingWebhookConfigurations", "name", wrc.getResourceValidatingWebhookConfigName())
|
||||
logger.Error(err, "unable to update validatingWebhookConfigurations", "name", getResourceValidatingWebhookConfigName(wrc.serverIP))
|
||||
go func() { wrc.UpdateWebhookChan <- true }()
|
||||
} else {
|
||||
logger.Info("successfully updated validatingWebhookConfigurations", "name", wrc.getResourceValidatingWebhookConfigName())
|
||||
logger.Info("successfully updated validatingWebhookConfigurations", "name", getResourceValidatingWebhookConfigName(wrc.serverIP))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,9 +326,9 @@ func (wrc *Register) createPolicyValidatingWebhookConfiguration(caData []byte) e
|
|||
var config *admregapi.ValidatingWebhookConfiguration
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
config = wrc.contructDebugPolicyValidatingWebhookConfig(caData)
|
||||
config = wrc.constructDebugPolicyValidatingWebhookConfig(caData)
|
||||
} else {
|
||||
config = wrc.contructPolicyValidatingWebhookConfig(caData)
|
||||
config = wrc.constructPolicyValidatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
if _, err := wrc.client.CreateResource("", kindValidating, "", *config, false); err != nil {
|
||||
|
@ -324,9 +348,9 @@ func (wrc *Register) createPolicyMutatingWebhookConfiguration(caData []byte) err
|
|||
var config *admregapi.MutatingWebhookConfiguration
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
config = wrc.contructDebugPolicyMutatingWebhookConfig(caData)
|
||||
config = wrc.constructDebugPolicyMutatingWebhookConfig(caData)
|
||||
} else {
|
||||
config = wrc.contructPolicyMutatingWebhookConfig(caData)
|
||||
config = wrc.constructPolicyMutatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
// create mutating webhook configuration resource
|
||||
|
@ -387,7 +411,7 @@ func (wrc *Register) removeWebhookConfigurations() {
|
|||
func (wrc *Register) removePolicyMutatingWebhookConfiguration(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
mutatingConfig := wrc.getPolicyMutatingWebhookConfigurationName()
|
||||
mutatingConfig := getPolicyMutatingWebhookConfigurationName(wrc.serverIP)
|
||||
|
||||
logger := wrc.log.WithValues("kind", kindMutating, "name", mutatingConfig)
|
||||
|
||||
|
@ -412,9 +436,9 @@ func (wrc *Register) removePolicyMutatingWebhookConfiguration(wg *sync.WaitGroup
|
|||
logger.Info("webhook configuration deleted")
|
||||
}
|
||||
|
||||
func (wrc *Register) getPolicyMutatingWebhookConfigurationName() string {
|
||||
func getPolicyMutatingWebhookConfigurationName(serverIP string) string {
|
||||
var mutatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
if serverIP != "" {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationName
|
||||
|
@ -425,7 +449,7 @@ func (wrc *Register) getPolicyMutatingWebhookConfigurationName() string {
|
|||
func (wrc *Register) removePolicyValidatingWebhookConfiguration(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
validatingConfig := wrc.getPolicyValidatingWebhookConfigurationName()
|
||||
validatingConfig := getPolicyValidatingWebhookConfigurationName(wrc.serverIP)
|
||||
|
||||
logger := wrc.log.WithValues("kind", kindValidating, "name", validatingConfig)
|
||||
if mutateCache, ok := wrc.resCache.GetGVRCache("ValidatingWebhookConfiguration"); ok {
|
||||
|
@ -450,9 +474,9 @@ func (wrc *Register) removePolicyValidatingWebhookConfiguration(wg *sync.WaitGro
|
|||
logger.Info("webhook configuration deleted")
|
||||
}
|
||||
|
||||
func (wrc *Register) getPolicyValidatingWebhookConfigurationName() string {
|
||||
func getPolicyValidatingWebhookConfigurationName(serverIP string) string {
|
||||
var validatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
if serverIP != "" {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationName
|
||||
|
@ -475,10 +499,13 @@ func (wrc *Register) constructVerifyMutatingWebhookConfig(caData []byte) *admreg
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"deployments/*"},
|
||||
"apps",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"deployments/*"},
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@ -499,10 +526,13 @@ func (wrc *Register) constructDebugVerifyMutatingWebhookConfig(caData []byte) *a
|
|||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"deployments/*"},
|
||||
"apps",
|
||||
"v1",
|
||||
admregapi.Rule{
|
||||
Resources: []string{"deployments/*"},
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
[]admregapi.OperationType{admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@ -597,7 +627,7 @@ func (wrc *Register) checkEndpoint() error {
|
|||
}
|
||||
|
||||
if podIp == "" {
|
||||
return fmt.Errorf("Pod is not assigned to any node yet")
|
||||
return fmt.Errorf("pod is not assigned to any node yet")
|
||||
}
|
||||
|
||||
for _, subset := range endpoint.Subsets {
|
||||
|
@ -616,7 +646,7 @@ func (wrc *Register) checkEndpoint() error {
|
|||
// clean up old webhook configurations, if any
|
||||
wrc.removeWebhookConfigurations()
|
||||
|
||||
err = fmt.Errorf("Endpoint not ready")
|
||||
err = fmt.Errorf("endpoint not ready")
|
||||
wrc.log.V(3).Info(err.Error(), "ns", config.KyvernoNamespace, "name", config.KyvernoServiceName)
|
||||
return err
|
||||
}
|
||||
|
@ -624,7 +654,7 @@ func (wrc *Register) checkEndpoint() error {
|
|||
func (wrc *Register) updateResourceValidatingWebhookConfiguration(nsSelector map[string]interface{}) error {
|
||||
validatingCache, _ := wrc.resCache.GetGVRCache(kindValidating)
|
||||
|
||||
resourceValidating, err := validatingCache.Lister().Get(wrc.getResourceValidatingWebhookConfigName())
|
||||
resourceValidating, err := validatingCache.Lister().Get(getResourceValidatingWebhookConfigName(wrc.serverIP))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get validatingWebhookConfigurations")
|
||||
}
|
||||
|
@ -660,7 +690,7 @@ func (wrc *Register) updateResourceValidatingWebhookConfiguration(nsSelector map
|
|||
func (wrc *Register) updateResourceMutatingWebhookConfiguration(nsSelector map[string]interface{}) error {
|
||||
mutatingCache, _ := wrc.resCache.GetGVRCache(kindMutating)
|
||||
|
||||
resourceMutating, err := mutatingCache.Lister().Get(wrc.getResourceMutatingWebhookConfigName())
|
||||
resourceMutating, err := mutatingCache.Lister().Get(getResourceMutatingWebhookConfigName(wrc.serverIP))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get mutatingWebhookConfigurations")
|
||||
}
|
||||
|
|
|
@ -5,12 +5,23 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
admregapi "k8s.io/api/admissionregistration/v1"
|
||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (wrc *Register) defaultResourceWebhookRule() admregapi.Rule {
|
||||
if wrc.autoUpdateWebhooks {
|
||||
return admregapi.Rule{}
|
||||
}
|
||||
|
||||
return admregapi.Rule{
|
||||
Resources: []string{"*/*"},
|
||||
APIGroups: []string{"*"},
|
||||
APIVersions: []string{"*"},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *Register) constructDefaultDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
logger := wrc.log
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
|
||||
|
@ -21,32 +32,30 @@ func (wrc *Register) constructDefaultDebugMutatingWebhookConfig(caData []byte) *
|
|||
},
|
||||
Webhooks: []admregapi.MutatingWebhook{
|
||||
generateDebugMutatingWebhook(
|
||||
config.MutatingWebhookName,
|
||||
config.MutatingWebhookName+"-ignore",
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"*/*"},
|
||||
"*",
|
||||
"*",
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
generateDebugMutatingWebhook(
|
||||
config.MutatingWebhookName+"-fail",
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Fail,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *Register) constructDefaultMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
|
||||
webhookCfg := generateMutatingWebhook(
|
||||
config.MutatingWebhookName,
|
||||
config.MutatingWebhookServicePath,
|
||||
caData, false, wrc.timeoutSeconds,
|
||||
[]string{"*/*"}, "*", "*",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update})
|
||||
|
||||
reinvoke := admregapi.IfNeededReinvocationPolicy
|
||||
webhookCfg.ReinvocationPolicy = &reinvoke
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationName,
|
||||
|
@ -54,13 +63,34 @@ func (wrc *Register) constructDefaultMutatingWebhookConfig(caData []byte) *admre
|
|||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.MutatingWebhook{webhookCfg},
|
||||
Webhooks: []admregapi.MutatingWebhook{
|
||||
generateMutatingWebhook(
|
||||
config.MutatingWebhookName+"-ignore",
|
||||
config.MutatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
generateMutatingWebhook(
|
||||
config.MutatingWebhookName+"-fail",
|
||||
config.MutatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
admregapi.Fail,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//getResourceMutatingWebhookConfigName returns the webhook configuration name
|
||||
func (wrc *Register) getResourceMutatingWebhookConfigName() string {
|
||||
if wrc.serverIP != "" {
|
||||
func getResourceMutatingWebhookConfigName(serverIP string) string {
|
||||
if serverIP != "" {
|
||||
return config.MutatingWebhookConfigurationDebugName
|
||||
}
|
||||
return config.MutatingWebhookConfigurationName
|
||||
|
@ -69,7 +99,7 @@ func (wrc *Register) getResourceMutatingWebhookConfigName() string {
|
|||
func (wrc *Register) removeResourceMutatingWebhookConfiguration(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
configName := wrc.getResourceMutatingWebhookConfigName()
|
||||
configName := getResourceMutatingWebhookConfigName(wrc.serverIP)
|
||||
logger := wrc.log.WithValues("kind", kindMutating, "name", configName)
|
||||
|
||||
if mutateCache, ok := wrc.resCache.GetGVRCache("MutatingWebhookConfiguration"); ok {
|
||||
|
@ -81,7 +111,7 @@ func (wrc *Register) removeResourceMutatingWebhookConfiguration(wg *sync.WaitGro
|
|||
|
||||
// delete webhook configuration
|
||||
err := wrc.client.DeleteResource("", kindMutating, "", configName, false)
|
||||
if errors.IsNotFound(err) {
|
||||
if errorsapi.IsNotFound(err) {
|
||||
logger.V(4).Info("webhook configuration not found")
|
||||
return
|
||||
}
|
||||
|
@ -103,15 +133,24 @@ func (wrc *Register) constructDefaultDebugValidatingWebhookConfig(caData []byte)
|
|||
},
|
||||
Webhooks: []admregapi.ValidatingWebhook{
|
||||
generateDebugValidatingWebhook(
|
||||
config.ValidatingWebhookName,
|
||||
config.ValidatingWebhookName+"-ignore",
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"*/*"},
|
||||
"*",
|
||||
"*",
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete, admregapi.Connect},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
generateDebugValidatingWebhook(
|
||||
config.ValidatingWebhookName+"-fail",
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete, admregapi.Connect},
|
||||
admregapi.Fail,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@ -127,23 +166,32 @@ func (wrc *Register) constructDefaultValidatingWebhookConfig(caData []byte) *adm
|
|||
},
|
||||
Webhooks: []admregapi.ValidatingWebhook{
|
||||
generateValidatingWebhook(
|
||||
config.ValidatingWebhookName,
|
||||
config.ValidatingWebhookName+"-ignore",
|
||||
config.ValidatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
[]string{"*/*"},
|
||||
"*",
|
||||
"*",
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete, admregapi.Connect},
|
||||
admregapi.Ignore,
|
||||
),
|
||||
generateValidatingWebhook(
|
||||
config.ValidatingWebhookName+"-fail",
|
||||
config.ValidatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
wrc.defaultResourceWebhookRule(),
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete, admregapi.Connect},
|
||||
admregapi.Fail,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// getResourceValidatingWebhookConfigName returns the webhook configuration name
|
||||
func (wrc *Register) getResourceValidatingWebhookConfigName() string {
|
||||
if wrc.serverIP != "" {
|
||||
func getResourceValidatingWebhookConfigName(serverIP string) string {
|
||||
if serverIP != "" {
|
||||
return config.ValidatingWebhookConfigurationDebugName
|
||||
}
|
||||
|
||||
|
@ -153,7 +201,7 @@ func (wrc *Register) getResourceValidatingWebhookConfigName() string {
|
|||
func (wrc *Register) removeResourceValidatingWebhookConfiguration(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
configName := wrc.getResourceValidatingWebhookConfigName()
|
||||
configName := getResourceValidatingWebhookConfigName(wrc.serverIP)
|
||||
logger := wrc.log.WithValues("kind", kindValidating, "name", configName)
|
||||
|
||||
if mutateCache, ok := wrc.resCache.GetGVRCache("ValidatingWebhookConfiguration"); ok {
|
||||
|
@ -164,7 +212,7 @@ func (wrc *Register) removeResourceValidatingWebhookConfiguration(wg *sync.WaitG
|
|||
}
|
||||
|
||||
err := wrc.client.DeleteResource("", kindValidating, "", configName, false)
|
||||
if errors.IsNotFound(err) {
|
||||
if errorsapi.IsNotFound(err) {
|
||||
logger.V(5).Info("webhook configuration not found")
|
||||
return
|
||||
}
|
||||
|
@ -175,5 +223,4 @@ func (wrc *Register) removeResourceValidatingWebhookConfiguration(wg *sync.WaitG
|
|||
}
|
||||
|
||||
logger.Info("webhook configuration deleted")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -116,8 +116,6 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
|
|||
|
||||
gr.SetNamespace(config.KyvernoNamespace)
|
||||
// Initial state "Pending"
|
||||
// TODO: status is not updated
|
||||
// gr.Status.State = kyverno.Pending
|
||||
// generate requests created in kyverno namespace
|
||||
isExist := false
|
||||
if action == v1beta1.Create || action == v1beta1.Update {
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: missing
|
||||
rule: validate-image-tag
|
||||
resource: test
|
||||
kind: Pod
|
||||
result: pass
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: missing
|
||||
kind: Pod
|
||||
result: pass
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: missing
|
||||
resource: test
|
||||
kind: Pod
|
||||
status: pass
|
||||
|
|
16
test/cli/test-mutate/patchedResource1.yaml
Normal file
16
test/cli/test-mutate/patchedResource1.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
name: resource-equal-to-patch-res-for-cp
|
||||
namespace: practice
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
25
test/cli/test-mutate/patchedResource10.yaml
Normal file
25
test/cli/test-mutate/patchedResource10.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mydeploy
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
10
test/cli/test-mutate/patchedResource11.yaml
Normal file
10
test/cli/test-mutate/patchedResource11.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-kind
|
||||
labels:
|
||||
foo: bar
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
12
test/cli/test-mutate/patchedResource2.yaml
Normal file
12
test/cli/test-mutate/patchedResource2.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-namespace
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
namespace: testing
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
12
test/cli/test-mutate/patchedResource3.yaml
Normal file
12
test/cli/test-mutate/patchedResource3.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-namespace
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
namespace: production
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
23
test/cli/test-mutate/patchedResource4.yaml
Normal file
23
test/cli/test-mutate/patchedResource4.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: mydeploy
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
color: orange
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.14.2
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
12
test/cli/test-mutate/patchedResource5.yaml
Normal file
12
test/cli/test-mutate/patchedResource5.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: same-name-but-diff-kind
|
||||
spec:
|
||||
selector:
|
||||
app: MyApp
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
nodePort: 30007
|
||||
type: NodePort
|
11
test/cli/test-mutate/patchedResource6.yaml
Normal file
11
test/cli/test-mutate/patchedResource6.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-kind
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
16
test/cli/test-mutate/patchedResource7.yaml
Normal file
16
test/cli/test-mutate/patchedResource7.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
name: resource-equal-to-patch-res-for-cp
|
||||
namespace: practice
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
15
test/cli/test-mutate/patchedResource8.yaml
Normal file
15
test/cli/test-mutate/patchedResource8.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
foo: bar
|
||||
name: same-name-but-diff-namespace
|
||||
namespace: testing
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
11
test/cli/test-mutate/patchedResource9.yaml
Normal file
11
test/cli/test-mutate/patchedResource9.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-namespace
|
||||
labels:
|
||||
foo: bar
|
||||
namespace: production
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
62
test/cli/test-mutate/policy.yaml
Normal file
62
test/cli/test-mutate/policy.yaml
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Below there are both type of olicies: ClusterPolicy and Policy(Namespaced-Policy)
|
||||
|
||||
#ClusterPolicy
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-label
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add nodeSelector
|
||||
policies.kyverno.io/category: Sample
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Labels are used as an important source of metadata describing objects in various ways
|
||||
or triggering other functionality. Labels are also a very basic concept and should be
|
||||
used throughout Kubernetes. This policy performs a simple mutation which adds a label
|
||||
`color=orange` to Pods, Services, ConfigMaps, and Secrets.
|
||||
spec:
|
||||
background: false
|
||||
validationFailureAction:
|
||||
rules:
|
||||
- name: add-label
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
color: orange
|
||||
|
||||
---
|
||||
|
||||
# Policy ( In testing namespace )
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: add-ndots
|
||||
namespace: testing
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add ndots
|
||||
policies.kyverno.io/category: Sample
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
The ndots value controls where DNS lookups are first performed in a cluster
|
||||
and needs to be set to a lower value than the default of 5 in some cases.
|
||||
This policy mutates all Pods to add the ndots option with a value of 1.
|
||||
spec:
|
||||
background: false
|
||||
rules:
|
||||
- name: add-ndots
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
spec:
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
100
test/cli/test-mutate/resource.yaml
Normal file
100
test/cli/test-mutate/resource.yaml
Normal file
|
@ -0,0 +1,100 @@
|
|||
# resource == patchedResource
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
name: resource-equal-to-patch-res-for-cp
|
||||
namespace: practice
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
|
||||
---
|
||||
# Resource with same name and diff. namespace
|
||||
# Same namespace as namespaced-policy
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-namespace
|
||||
labels:
|
||||
foo: bar
|
||||
namespace: testing
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
|
||||
---
|
||||
# Resource with same name and diff. namespace
|
||||
# Namespace differ from namespaced-policy
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-namespace
|
||||
labels:
|
||||
foo: bar
|
||||
namespace: production
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
|
||||
---
|
||||
|
||||
# Deployment in default namespace
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mydeploy
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
---
|
||||
|
||||
# Resource (Service) with same name but different kind
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: same-name-but-diff-kind
|
||||
spec:
|
||||
selector:
|
||||
app: MyApp
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
nodePort: 30007
|
||||
type: NodePort
|
||||
|
||||
---
|
||||
|
||||
# Resource (Pod) with same name but different kind
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: same-name-but-diff-kind
|
||||
labels:
|
||||
foo: bar
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
|
||||
|
86
test/cli/test-mutate/test.yaml
Normal file
86
test/cli/test-mutate/test.yaml
Normal file
|
@ -0,0 +1,86 @@
|
|||
name: add-nodeselector
|
||||
policies:
|
||||
- policy.yaml
|
||||
resources:
|
||||
- resource.yaml
|
||||
results:
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: resource-equal-to-patch-res-for-cp
|
||||
patchedResource: patchedResource1.yaml
|
||||
kind: Pod
|
||||
namespace: practice
|
||||
result: skip
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: same-name-but-diff-namespace
|
||||
patchedResource: patchedResource2.yaml
|
||||
kind: Pod
|
||||
namespace: testing
|
||||
result: pass
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: same-name-but-diff-namespace
|
||||
patchedResource: patchedResource3.yaml
|
||||
kind: Pod
|
||||
namespace: production
|
||||
result: pass
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: mydeploy
|
||||
patchedResource: patchedResource4.yaml
|
||||
kind: Deployment
|
||||
result: pass
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: same-name-but-diff-kind
|
||||
patchedResource: patchedResource5.yaml
|
||||
kind: Service
|
||||
result: skip
|
||||
- policy: add-label
|
||||
rule: add-label
|
||||
resource: same-name-but-diff-kind
|
||||
patchedResource: patchedResource6.yaml
|
||||
kind: Pod
|
||||
result: pass
|
||||
|
||||
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: resource-equal-to-patch-res-for-cp
|
||||
namespace: practice
|
||||
patchedResource: patchedResource7.yaml
|
||||
kind: Pod
|
||||
result: skip
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: same-name-but-diff-namespace
|
||||
patchedResource: patchedResource8.yaml
|
||||
namespace: testing
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: same-name-but-diff-namespace
|
||||
patchedResource: patchedResource9.yaml
|
||||
kind: Pod
|
||||
namespace: production
|
||||
result: skip
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: mydeploy
|
||||
patchedResource: patchedResource10.yaml
|
||||
kind: Deployment
|
||||
result: skip
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: same-name-but-diff-kind
|
||||
patchedResource: patchedResource5.yaml
|
||||
kind: Service
|
||||
result: skip
|
||||
- policy: testing/add-ndots
|
||||
rule: add-ndots
|
||||
resource: same-name-but-diff-kind
|
||||
patchedResource: patchedResource11.yaml
|
||||
kind: Pod
|
||||
result: skip
|
|
@ -7,46 +7,54 @@ results:
|
|||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: Pod
|
||||
resource: pod-with-labels
|
||||
|
||||
# TEST: Pod Missing Labels Should Fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: Pod
|
||||
resource: pod-missing-labels
|
||||
|
||||
# TEST: Deployment with Labels Should Pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: Deployment
|
||||
resource: deployment-with-labels
|
||||
|
||||
# TEST: Deployment with Labels Should Fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: skip
|
||||
kind: Deployment
|
||||
resource: deployment-missing-labels
|
||||
|
||||
# TEST: StatefulSet with Labels Should Pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: StatefulSet
|
||||
resource: StatefulSet-with-labels
|
||||
|
||||
# TEST: StatefulSet with Labels Should fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: StatefulSet
|
||||
resource: StatefulSet-without-labels
|
||||
|
||||
# TEST: Cronjob with Labels Should pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: CronJob
|
||||
resource: cronjob-with-labels
|
||||
|
||||
# TEST: Cronjob without Labels Should fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: CronJob
|
||||
resource: cronjob-without-labels
|
||||
|
|
|
@ -7,20 +7,25 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: require-image-tag
|
||||
resource: test-require-image-tag-pass
|
||||
kind: Pod
|
||||
status: pass
|
||||
- policy: disallow-latest-tag
|
||||
rule: require-image-tag
|
||||
resource: test-require-image-tag-fail
|
||||
kind: Pod
|
||||
status: fail
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-ignore
|
||||
kind: Pod
|
||||
status: skip
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-fail
|
||||
kind: Pod
|
||||
status: fail
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-pass
|
||||
kind: Pod
|
||||
status: pass
|
||||
|
|
|
@ -11,32 +11,40 @@ results:
|
|||
- policy: cm-variable-example
|
||||
rule: example-configmap-lookup
|
||||
resource: test-env-test
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-variable-example
|
||||
rule: example-configmap-lookup
|
||||
resource: test-env-dev
|
||||
kind: Pod
|
||||
result: fail
|
||||
- policy: cm-array-example
|
||||
rule: validate-role-annotation
|
||||
resource: test-web
|
||||
kind: Pod
|
||||
result: fail
|
||||
- policy: cm-array-example
|
||||
rule: validate-role-annotation
|
||||
resource: test-app
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-blk-scalar-example
|
||||
rule: validate-blk-role-annotation
|
||||
resource: test-blk-web
|
||||
kind: Pod
|
||||
result: fail
|
||||
- policy: cm-blk-scalar-example
|
||||
rule: validate-blk-role-annotation
|
||||
resource: test-blk-app
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-globalval-example
|
||||
rule: validate-mode
|
||||
resource: test-global-dev
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-globalval-example
|
||||
rule: validate-mode
|
||||
resource: test-global-prod
|
||||
kind: Pod
|
||||
result: fail
|
||||
|
|
|
@ -34,7 +34,7 @@ func CallMetrics() (string, error) {
|
|||
func ProcessMetrics(newStr, e2ePolicyName string) error {
|
||||
splitByNewLine := strings.Split(newStr, "\n")
|
||||
for _, lineSplitByNewLine := range splitByNewLine {
|
||||
// kyverno_policy_rule_info_total{policy_background_mode=\"false\",policy_name=\"gen-cluster-policy\",policy_namespace=\"-\",policy_type=\"cluster\",policy_validation_mode=\"audit\",rule_name=\"gen-cluster-role\",rule_type=\"generate\"} 1
|
||||
// kyverno_policy_rule_info_total{policy_background_mode=\"false\",policy_name=\"gen-cluster-policy\",policy_namespace=\"-\",policy_type=\"cluster\",policy_validation_mode=\"audit\",rule_name=\"gen-cluster-role\",rule_type=\"generate\",status_ready="false"} 1
|
||||
if !strings.HasPrefix(lineSplitByNewLine, "kyverno_policy_rule_info_total{") {
|
||||
continue
|
||||
}
|
||||
|
@ -48,10 +48,18 @@ func ProcessMetrics(newStr, e2ePolicyName string) error {
|
|||
if strings.HasPrefix(lineSplitByComma, "policy_name=") {
|
||||
splitByQuote := strings.Split(lineSplitByComma, "\"")
|
||||
policyName := splitByQuote[1]
|
||||
if policyName == e2ePolicyName {
|
||||
if policyName != e2ePolicyName {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(lineSplitByComma, "status_ready=") {
|
||||
splitByQuote := strings.Split(lineSplitByComma, "\"")
|
||||
status := splitByQuote[1]
|
||||
if status == "true" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue