mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
feat: use admission review v1 (#5464)
* feat: use admission review v1 Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * nit Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * logs Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * patch type Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com> Co-authored-by: Prateek Pandey <prateek.pandey@nirmata.com>
This commit is contained in:
parent
1ea4a0db19
commit
83bbf87ff6
12 changed files with 64 additions and 52 deletions
1
.github/workflows/e2e.yaml
vendored
1
.github/workflows/e2e.yaml
vendored
|
@ -91,3 +91,4 @@ jobs:
|
||||||
kubectl -n kyverno describe pod | grep -i events -A10
|
kubectl -n kyverno describe pod | grep -i events -A10
|
||||||
kubectl -n kyverno logs deploy/kyverno -p || true
|
kubectl -n kyverno logs deploy/kyverno -p || true
|
||||||
kubectl -n kyverno logs deploy/kyverno
|
kubectl -n kyverno logs deploy/kyverno
|
||||||
|
kubectl -n kube-system logs kube-apiserver-kind-control-plane
|
||||||
|
|
|
@ -25,11 +25,11 @@ func (h *cleanupPolicyHandlers) Validate(ctx context.Context, logger logr.Logger
|
||||||
policy, _, err := admissionutils.GetCleanupPolicies(request)
|
policy, _, err := admissionutils.GetCleanupPolicies(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "failed to unmarshal policies from admission request")
|
logger.Error(err, "failed to unmarshal policies from admission request")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
err = validate.ValidateCleanupPolicy(logger, policy, h.client, false)
|
err = validate.ValidateCleanupPolicy(logger, policy, h.client, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "policy validation errors")
|
logger.Error(err, "policy validation errors")
|
||||||
}
|
}
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -526,7 +526,7 @@ func (c *controller) buildVerifyMutatingWebhookConfiguration(caBundle []byte) (*
|
||||||
FailurePolicy: &ignore,
|
FailurePolicy: &ignore,
|
||||||
SideEffects: &noneOnDryRun,
|
SideEffects: &noneOnDryRun,
|
||||||
ReinvocationPolicy: &ifNeeded,
|
ReinvocationPolicy: &ifNeeded,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
ObjectSelector: &metav1.LabelSelector{
|
ObjectSelector: &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{
|
MatchLabels: map[string]string{
|
||||||
"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
|
"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
|
||||||
|
@ -553,7 +553,7 @@ func (c *controller) buildPolicyMutatingWebhookConfiguration(caBundle []byte) (*
|
||||||
FailurePolicy: &ignore,
|
FailurePolicy: &ignore,
|
||||||
SideEffects: &noneOnDryRun,
|
SideEffects: &noneOnDryRun,
|
||||||
ReinvocationPolicy: &ifNeeded,
|
ReinvocationPolicy: &ifNeeded,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
nil
|
nil
|
||||||
|
@ -574,7 +574,7 @@ func (c *controller) buildPolicyValidatingWebhookConfiguration(caBundle []byte)
|
||||||
}},
|
}},
|
||||||
FailurePolicy: &ignore,
|
FailurePolicy: &ignore,
|
||||||
SideEffects: &none,
|
SideEffects: &none,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
nil
|
nil
|
||||||
|
@ -599,7 +599,7 @@ func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(caBundle [
|
||||||
}},
|
}},
|
||||||
FailurePolicy: &ignore,
|
FailurePolicy: &ignore,
|
||||||
SideEffects: &noneOnDryRun,
|
SideEffects: &noneOnDryRun,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
TimeoutSeconds: &c.defaultTimeout,
|
TimeoutSeconds: &c.defaultTimeout,
|
||||||
ReinvocationPolicy: &ifNeeded,
|
ReinvocationPolicy: &ifNeeded,
|
||||||
}},
|
}},
|
||||||
|
@ -651,7 +651,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(caBundle []byte)
|
||||||
Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
|
Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
|
||||||
FailurePolicy: &ignore.failurePolicy,
|
FailurePolicy: &ignore.failurePolicy,
|
||||||
SideEffects: &noneOnDryRun,
|
SideEffects: &noneOnDryRun,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
NamespaceSelector: webhookCfg.NamespaceSelector,
|
NamespaceSelector: webhookCfg.NamespaceSelector,
|
||||||
ObjectSelector: webhookCfg.ObjectSelector,
|
ObjectSelector: webhookCfg.ObjectSelector,
|
||||||
TimeoutSeconds: &ignore.maxWebhookTimeout,
|
TimeoutSeconds: &ignore.maxWebhookTimeout,
|
||||||
|
@ -668,7 +668,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(caBundle []byte)
|
||||||
Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
|
Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
|
||||||
FailurePolicy: &fail.failurePolicy,
|
FailurePolicy: &fail.failurePolicy,
|
||||||
SideEffects: &noneOnDryRun,
|
SideEffects: &noneOnDryRun,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
NamespaceSelector: webhookCfg.NamespaceSelector,
|
NamespaceSelector: webhookCfg.NamespaceSelector,
|
||||||
ObjectSelector: webhookCfg.ObjectSelector,
|
ObjectSelector: webhookCfg.ObjectSelector,
|
||||||
TimeoutSeconds: &fail.maxWebhookTimeout,
|
TimeoutSeconds: &fail.maxWebhookTimeout,
|
||||||
|
@ -707,7 +707,7 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(caBundle
|
||||||
}},
|
}},
|
||||||
FailurePolicy: &ignore,
|
FailurePolicy: &ignore,
|
||||||
SideEffects: sideEffects,
|
SideEffects: sideEffects,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
TimeoutSeconds: &c.defaultTimeout,
|
TimeoutSeconds: &c.defaultTimeout,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
@ -762,7 +762,7 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(caBundle []byte
|
||||||
Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
|
Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
|
||||||
FailurePolicy: &ignore.failurePolicy,
|
FailurePolicy: &ignore.failurePolicy,
|
||||||
SideEffects: sideEffects,
|
SideEffects: sideEffects,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
NamespaceSelector: webhookCfg.NamespaceSelector,
|
NamespaceSelector: webhookCfg.NamespaceSelector,
|
||||||
ObjectSelector: webhookCfg.ObjectSelector,
|
ObjectSelector: webhookCfg.ObjectSelector,
|
||||||
TimeoutSeconds: &ignore.maxWebhookTimeout,
|
TimeoutSeconds: &ignore.maxWebhookTimeout,
|
||||||
|
@ -778,7 +778,7 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(caBundle []byte
|
||||||
Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
|
Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
|
||||||
FailurePolicy: &fail.failurePolicy,
|
FailurePolicy: &fail.failurePolicy,
|
||||||
SideEffects: sideEffects,
|
SideEffects: sideEffects,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1"},
|
||||||
NamespaceSelector: webhookCfg.NamespaceSelector,
|
NamespaceSelector: webhookCfg.NamespaceSelector,
|
||||||
ObjectSelector: webhookCfg.ObjectSelector,
|
ObjectSelector: webhookCfg.ObjectSelector,
|
||||||
TimeoutSeconds: &fail.maxWebhookTimeout,
|
TimeoutSeconds: &fail.maxWebhookTimeout,
|
||||||
|
|
|
@ -3,11 +3,15 @@ package admission
|
||||||
import (
|
import (
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Response(err error, warnings ...string) *admissionv1.AdmissionResponse {
|
var patchTypeJSONPatch = admissionv1.PatchTypeJSONPatch
|
||||||
|
|
||||||
|
func Response(uid types.UID, err error, warnings ...string) *admissionv1.AdmissionResponse {
|
||||||
response := &admissionv1.AdmissionResponse{
|
response := &admissionv1.AdmissionResponse{
|
||||||
Allowed: err == nil,
|
Allowed: err == nil,
|
||||||
|
UID: uid,
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Result = &metav1.Status{
|
response.Result = &metav1.Status{
|
||||||
|
@ -19,12 +23,15 @@ func Response(err error, warnings ...string) *admissionv1.AdmissionResponse {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResponseSuccess(warnings ...string) *admissionv1.AdmissionResponse {
|
func ResponseSuccess(uid types.UID, warnings ...string) *admissionv1.AdmissionResponse {
|
||||||
return Response(nil, warnings...)
|
return Response(uid, nil, warnings...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MutationResponse(patch []byte, warnings ...string) *admissionv1.AdmissionResponse {
|
func MutationResponse(uid types.UID, patch []byte, warnings ...string) *admissionv1.AdmissionResponse {
|
||||||
response := ResponseSuccess(warnings...)
|
response := ResponseSuccess(uid, warnings...)
|
||||||
response.Patch = patch
|
if len(patch) != 0 {
|
||||||
|
response.Patch = patch
|
||||||
|
response.PatchType = &patchTypeJSONPatch
|
||||||
|
}
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ func TestResponse(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := Response(tt.args.err, tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
if got := Response("", tt.args.err, tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("Response() = %v, want %v", got, tt.want)
|
t.Errorf("Response() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -102,7 +102,7 @@ func TestResponseSuccess(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := ResponseSuccess(tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
if got := ResponseSuccess("", tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("ResponseSuccess() = %v, want %v", got, tt.want)
|
t.Errorf("ResponseSuccess() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -144,8 +144,9 @@ func TestMutationResponse(t *testing.T) {
|
||||||
warnings: nil,
|
warnings: nil,
|
||||||
},
|
},
|
||||||
want: &admissionv1.AdmissionResponse{
|
want: &admissionv1.AdmissionResponse{
|
||||||
Allowed: true,
|
Allowed: true,
|
||||||
Patch: []byte{1, 2, 3, 4},
|
Patch: []byte{1, 2, 3, 4},
|
||||||
|
PatchType: &patchTypeJSONPatch,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "patch, warnings",
|
name: "patch, warnings",
|
||||||
|
@ -154,14 +155,15 @@ func TestMutationResponse(t *testing.T) {
|
||||||
warnings: []string{"foo", "bar"},
|
warnings: []string{"foo", "bar"},
|
||||||
},
|
},
|
||||||
want: &admissionv1.AdmissionResponse{
|
want: &admissionv1.AdmissionResponse{
|
||||||
Allowed: true,
|
Allowed: true,
|
||||||
Patch: []byte{1, 2, 3, 4},
|
Patch: []byte{1, 2, 3, 4},
|
||||||
Warnings: []string{"foo", "bar"},
|
Warnings: []string{"foo", "bar"},
|
||||||
|
PatchType: &patchTypeJSONPatch,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := MutationResponse(tt.args.patch, tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
if got := MutationResponse("", tt.args.patch, tt.args.warnings...); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("MutationResponse() = %v, want %v", got, tt.want)
|
t.Errorf("MutationResponse() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,23 +19,23 @@ func (inner AdmissionHandler) withAdmission(logger logr.Logger) HttpHandler {
|
||||||
return func(writer http.ResponseWriter, request *http.Request) {
|
return func(writer http.ResponseWriter, request *http.Request) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
if request.Body == nil {
|
if request.Body == nil {
|
||||||
httpError(writer, request, logger, errors.New("empty body"), http.StatusBadRequest)
|
HttpError(request.Context(), writer, request, logger, errors.New("empty body"), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer request.Body.Close()
|
defer request.Body.Close()
|
||||||
body, err := io.ReadAll(request.Body)
|
body, err := io.ReadAll(request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(writer, request, logger, err, http.StatusBadRequest)
|
HttpError(request.Context(), writer, request, logger, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
contentType := request.Header.Get("Content-Type")
|
contentType := request.Header.Get("Content-Type")
|
||||||
if contentType != "application/json" {
|
if contentType != "application/json" {
|
||||||
httpError(writer, request, logger, errors.New("invalid Content-Type"), http.StatusUnsupportedMediaType)
|
HttpError(request.Context(), writer, request, logger, errors.New("invalid Content-Type"), http.StatusUnsupportedMediaType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
admissionReview := &admissionv1.AdmissionReview{}
|
admissionReview := &admissionv1.AdmissionReview{}
|
||||||
if err := json.Unmarshal(body, &admissionReview); err != nil {
|
if err := json.Unmarshal(body, &admissionReview); err != nil {
|
||||||
httpError(writer, request, logger, err, http.StatusExpectationFailed)
|
HttpError(request.Context(), writer, request, logger, err, http.StatusExpectationFailed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger := logger.WithValues(
|
logger := logger.WithValues(
|
||||||
|
@ -56,12 +56,12 @@ func (inner AdmissionHandler) withAdmission(logger logr.Logger) HttpHandler {
|
||||||
}
|
}
|
||||||
responseJSON, err := json.Marshal(admissionReview)
|
responseJSON, err := json.Marshal(admissionReview)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(writer, request, logger, err, http.StatusInternalServerError)
|
HttpError(request.Context(), writer, request, logger, err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
if _, err := writer.Write(responseJSON); err != nil {
|
if _, err := writer.Write(responseJSON); err != nil {
|
||||||
httpError(writer, request, logger, err, http.StatusInternalServerError)
|
HttpError(request.Context(), writer, request, logger, err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if admissionReview.Request.Kind.Kind == "Lease" {
|
if admissionReview.Request.Kind.Kind == "Lease" {
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/kyverno/kyverno/pkg/tracing"
|
"github.com/kyverno/kyverno/pkg/tracing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func httpError(writer http.ResponseWriter, request *http.Request, logger logr.Logger, err error, code int) {
|
func HttpError(ctx context.Context, writer http.ResponseWriter, request *http.Request, logger logr.Logger, err error, code int) {
|
||||||
logger.Error(err, "an error has occurred", "url", request.URL.String())
|
logger.Error(err, "an error has occurred", "url", request.URL.String())
|
||||||
tracing.SetHttpStatus(request.Context(), err, code)
|
tracing.SetHttpStatus(ctx, err, code)
|
||||||
http.Error(writer, err.Error(), code)
|
http.Error(writer, err.Error(), code)
|
||||||
}
|
}
|
|
@ -27,14 +27,14 @@ func (inner AdmissionHandler) withProtection() AdmissionHandler {
|
||||||
newResource, oldResource, err := utils.ExtractResources(nil, request)
|
newResource, oldResource, err := utils.ExtractResources(nil, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "Failed to extract resources")
|
logger.Error(err, "Failed to extract resources")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
for _, resource := range []unstructured.Unstructured{newResource, oldResource} {
|
for _, resource := range []unstructured.Unstructured{newResource, oldResource} {
|
||||||
resLabels := resource.GetLabels()
|
resLabels := resource.GetLabels()
|
||||||
if resLabels[kyvernov1.LabelAppManagedBy] == kyvernov1.ValueKyvernoApp {
|
if resLabels[kyvernov1.LabelAppManagedBy] == kyvernov1.ValueKyvernoApp {
|
||||||
if request.UserInfo.Username != fmt.Sprintf("system:serviceaccount:%s:%s", config.KyvernoNamespace(), config.KyvernoServiceAccountName()) {
|
if request.UserInfo.Username != fmt.Sprintf("system:serviceaccount:%s:%s", config.KyvernoNamespace(), config.KyvernoServiceAccountName()) {
|
||||||
logger.Info("Access to the resource not authorized, this is a kyverno managed resource and should be altered only by kyverno")
|
logger.Info("Access to the resource not authorized, this is a kyverno managed resource and should be altered only by kyverno")
|
||||||
return admissionutils.Response(errors.New("A kyverno managed resource can only be modified by kyverno"))
|
return admissionutils.Response(request.UID, errors.New("A kyverno managed resource can only be modified by kyverno"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ import (
|
||||||
|
|
||||||
func Verify(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
|
func Verify(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
|
||||||
if request.Name != "kyverno-health" || request.Namespace != config.KyvernoNamespace() {
|
if request.Name != "kyverno-health" || request.Namespace != config.KyvernoNamespace() {
|
||||||
return admissionutils.ResponseSuccess()
|
return admissionutils.ResponseSuccess(request.UID)
|
||||||
}
|
}
|
||||||
patch := jsonutils.NewPatchOperation("/metadata/annotations/"+"kyverno.io~1last-request-time", "replace", time.Now().Format(time.RFC3339))
|
patch := jsonutils.NewPatchOperation("/metadata/annotations/"+"kyverno.io~1last-request-time", "replace", time.Now().Format(time.RFC3339))
|
||||||
bytes, err := patch.ToPatchBytes()
|
bytes, err := patch.ToPatchBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "failed to build patch bytes")
|
logger.Error(err, "failed to build patch bytes")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
return admissionutils.MutationResponse(bytes)
|
return admissionutils.MutationResponse(request.UID, bytes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,15 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
|
||||||
policy, _, err := admissionutils.GetPolicies(request)
|
policy, _, err := admissionutils.GetPolicies(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "failed to unmarshal policies from admission request")
|
logger.Error(err, "failed to unmarshal policies from admission request")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
warnings, err := policyvalidate.Validate(policy, h.client, false, h.openApiManager)
|
warnings, err := policyvalidate.Validate(policy, h.client, false, h.openApiManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "policy validation errors")
|
logger.Error(err, "policy validation errors")
|
||||||
}
|
}
|
||||||
return admissionutils.Response(err, warnings...)
|
return admissionutils.Response(request.UID, err, warnings...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handlers) Mutate(_ context.Context, _ logr.Logger, _ *admissionv1.AdmissionRequest, _ time.Time) *admissionv1.AdmissionResponse {
|
func (h *handlers) Mutate(_ context.Context, _ logr.Logger, _ *admissionv1.AdmissionRequest, _ time.Time) *admissionv1.AdmissionResponse {
|
||||||
return admissionutils.ResponseSuccess()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
|
||||||
|
|
||||||
policyContext, err := h.pcBuilder.Build(request, generatePolicies...)
|
policyContext, err := h.pcBuilder.Build(request, generatePolicies...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorResponse(logger, err, "failed create policy context")
|
return errorResponse(logger, request.UID, err, "failed create policy context")
|
||||||
}
|
}
|
||||||
|
|
||||||
namespaceLabels := make(map[string]string)
|
namespaceLabels := make(map[string]string)
|
||||||
|
@ -132,13 +132,13 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
|
||||||
ok, msg, warnings := vh.HandleValidation(h.metricsConfig, request, policies, policyContext, namespaceLabels, startTime)
|
ok, msg, warnings := vh.HandleValidation(h.metricsConfig, request, policies, policyContext, namespaceLabels, startTime)
|
||||||
if !ok {
|
if !ok {
|
||||||
logger.Info("admission request denied")
|
logger.Info("admission request denied")
|
||||||
return admissionutils.Response(errors.New(msg), warnings...)
|
return admissionutils.Response(request.UID, errors.New(msg), warnings...)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer h.handleDelete(logger, request)
|
defer h.handleDelete(logger, request)
|
||||||
go h.createUpdateRequests(logger, request, policyContext, generatePolicies, mutatePolicies, startTime)
|
go h.createUpdateRequests(logger, request, policyContext, generatePolicies, mutatePolicies, startTime)
|
||||||
|
|
||||||
return admissionutils.ResponseSuccess(warnings...)
|
return admissionutils.ResponseSuccess(request.UID, warnings...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, failurePolicy string, startTime time.Time) *admissionv1.AdmissionResponse {
|
func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, failurePolicy string, startTime time.Time) *admissionv1.AdmissionResponse {
|
||||||
|
@ -149,13 +149,13 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
|
||||||
verifyImagesPolicies := filterPolicies(failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesMutate, kind, request.Namespace)...)
|
verifyImagesPolicies := filterPolicies(failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesMutate, kind, request.Namespace)...)
|
||||||
if len(mutatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
|
if len(mutatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
|
||||||
logger.V(4).Info("no policies matched mutate admission request")
|
logger.V(4).Info("no policies matched mutate admission request")
|
||||||
return admissionutils.ResponseSuccess()
|
return admissionutils.ResponseSuccess(request.UID)
|
||||||
}
|
}
|
||||||
logger.V(4).Info("processing policies for mutate admission request", "mutatePolicies", len(mutatePolicies), "verifyImagesPolicies", len(verifyImagesPolicies))
|
logger.V(4).Info("processing policies for mutate admission request", "mutatePolicies", len(mutatePolicies), "verifyImagesPolicies", len(verifyImagesPolicies))
|
||||||
policyContext, err := h.pcBuilder.Build(request, mutatePolicies...)
|
policyContext, err := h.pcBuilder.Build(request, mutatePolicies...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "failed to build policy context")
|
logger.Error(err, "failed to build policy context")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
// update container images to a canonical form
|
// update container images to a canonical form
|
||||||
if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext); err != nil {
|
if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext); err != nil {
|
||||||
|
@ -165,26 +165,26 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
|
||||||
mutatePatches, mutateWarnings, err := mh.HandleMutation(h.metricsConfig, request, mutatePolicies, policyContext, startTime)
|
mutatePatches, mutateWarnings, err := mh.HandleMutation(h.metricsConfig, request, mutatePolicies, policyContext, startTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "mutation failed")
|
logger.Error(err, "mutation failed")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
newRequest := patchRequest(mutatePatches, request, logger)
|
newRequest := patchRequest(mutatePatches, request, logger)
|
||||||
// rebuild context to process images updated via mutate policies
|
// rebuild context to process images updated via mutate policies
|
||||||
policyContext, err = h.pcBuilder.Build(newRequest, mutatePolicies...)
|
policyContext, err = h.pcBuilder.Build(newRequest, mutatePolicies...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "failed to build policy context")
|
logger.Error(err, "failed to build policy context")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.eventGen, h.admissionReports)
|
ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.eventGen, h.admissionReports)
|
||||||
imagePatches, imageVerifyWarnings, err := ivh.Handle(h.metricsConfig, newRequest, verifyImagesPolicies, policyContext)
|
imagePatches, imageVerifyWarnings, err := ivh.Handle(h.metricsConfig, newRequest, verifyImagesPolicies, policyContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "image verification failed")
|
logger.Error(err, "image verification failed")
|
||||||
return admissionutils.Response(err)
|
return admissionutils.Response(request.UID, err)
|
||||||
}
|
}
|
||||||
patch := jsonutils.JoinPatches(mutatePatches, imagePatches)
|
patch := jsonutils.JoinPatches(mutatePatches, imagePatches)
|
||||||
var warnings []string
|
var warnings []string
|
||||||
warnings = append(warnings, mutateWarnings...)
|
warnings = append(warnings, mutateWarnings...)
|
||||||
warnings = append(warnings, imageVerifyWarnings...)
|
warnings = append(warnings, imageVerifyWarnings...)
|
||||||
return admissionutils.MutationResponse(patch, warnings...)
|
return admissionutils.MutationResponse(request.UID, patch, warnings...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handlers) handleDelete(logger logr.Logger, request *admissionv1.AdmissionRequest) {
|
func (h *handlers) handleDelete(logger logr.Logger, request *admissionv1.AdmissionRequest) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
||||||
"github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
|
"github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type updateRequestResponse struct {
|
type updateRequestResponse struct {
|
||||||
|
@ -18,9 +19,9 @@ type updateRequestResponse struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorResponse(logger logr.Logger, err error, message string) *admissionv1.AdmissionResponse {
|
func errorResponse(logger logr.Logger, uid types.UID, err error, message string) *admissionv1.AdmissionResponse {
|
||||||
logger.Error(err, message)
|
logger.Error(err, message)
|
||||||
return admissionutils.Response(errors.New(message + ": " + err.Error()))
|
return admissionutils.Response(uid, errors.New(message+": "+err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func patchRequest(patches []byte, request *admissionv1.AdmissionRequest, logger logr.Logger) *admissionv1.AdmissionRequest {
|
func patchRequest(patches []byte, request *admissionv1.AdmissionRequest, logger logr.Logger) *admissionv1.AdmissionRequest {
|
||||||
|
|
Loading…
Add table
Reference in a new issue