diff --git a/main.go b/main.go index 9839f1f1a6..65a36e7ff8 100644 --- a/main.go +++ b/main.go @@ -155,6 +155,11 @@ func main() { setupLog.Error(err, "unable to init kruise clientset and informer") os.Exit(1) } + err = util.InitProtectionLogger() + if err != nil { + setupLog.Error(err, "unable to init protection logger") + os.Exit(1) + } var syncPeriod *time.Duration if syncPeriodStr != "" { diff --git a/pkg/control/pubcontrol/pub_control_utils.go b/pkg/control/pubcontrol/pub_control_utils.go index 7249f00315..8ef8ef1a28 100644 --- a/pkg/control/pubcontrol/pub_control_utils.go +++ b/pkg/control/pubcontrol/pub_control_utils.go @@ -148,6 +148,7 @@ func PodUnavailableBudgetValidatePod(pod *corev1.Pod, operation policyv1alpha1.P } PodUnavailableBudgetMetrics.WithLabelValues(fmt.Sprintf("%s_%s_%s", kind, namespace, name), username).Add(1) recorder.Eventf(pod, corev1.EventTypeWarning, "PubPreventPodDeletion", "openkruise pub prevents pod deletion") + util.LoggerProtectionInfo(util.ProtectionEventPub, kind, namespace, name, username) return err } diff --git a/pkg/util/logger.go b/pkg/util/logger.go new file mode 100644 index 0000000000..3e34196c91 --- /dev/null +++ b/pkg/util/logger.go @@ -0,0 +1,77 @@ +/* +Copyright 2023 The Kruise Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "os" + "path/filepath" +) + +var ( + protectionLogger *log.Logger + protectionLogPath string +) + +const ( + ProtectionEventPub = "PodUnavailableBudget" + ProtectionEventDeletionProtection = "DeletionProtection" +) + +type ProtectionLoggerInfo struct { + // PUB, ProtectionDeletion + Event string + Kind string + Namespace string + Name string + UserAgent string +} + +func init() { + flag.StringVar(&protectionLogPath, "protection-log-path", "/log", "protection log path, for example pub, delete_protection") +} +func InitProtectionLogger() error { + err := os.MkdirAll(protectionLogPath, 0644) + if err != nil { + return fmt.Errorf("MkdirAll(%s) failed: %s", protectionLogPath, err.Error()) + } + file, err := os.OpenFile(filepath.Join(protectionLogPath, "protection.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return fmt.Errorf("openFile(%s) failed: %s", filepath.Join(protectionLogPath, "protection.log"), err.Error()) + } + protectionLogger = log.New(file, "", 0) + return nil +} + +func LoggerProtectionInfo(event, kind, ns, name, userAgent string) { + // compatible with go test + if protectionLogger == nil { + return + } + info := ProtectionLoggerInfo{ + Event: event, + Kind: kind, + Namespace: ns, + Name: name, + UserAgent: userAgent, + } + by, _ := json.Marshal(info) + protectionLogger.Println(string(by)) +} diff --git a/pkg/webhook/builtinworkloads/validating/builtin_handlers.go b/pkg/webhook/builtinworkloads/validating/builtin_handlers.go index 71f69693ce..9c599b4e45 100644 --- a/pkg/webhook/builtinworkloads/validating/builtin_handlers.go +++ b/pkg/webhook/builtinworkloads/validating/builtin_handlers.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" admissionv1 "k8s.io/api/admission/v1" apps "k8s.io/api/apps/v1" @@ -81,6 +82,7 @@ func (h *WorkloadHandler) Handle(ctx context.Context, req admission.Request) adm if err := deletionprotection.ValidateWorkloadDeletion(metaObj, replicas); err != nil { deletionprotection.WorkloadDeletionProtectionMetrics.WithLabelValues(fmt.Sprintf("%s_%s_%s", req.Kind.Kind, metaObj.GetNamespace(), metaObj.GetName()), req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, req.Kind.Kind, metaObj.GetNamespace(), metaObj.GetName(), req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } return admission.ValidationResponse(true, "") diff --git a/pkg/webhook/cloneset/validating/cloneset_create_update_handler.go b/pkg/webhook/cloneset/validating/cloneset_create_update_handler.go index 84209898f8..bb17165037 100644 --- a/pkg/webhook/cloneset/validating/cloneset_create_update_handler.go +++ b/pkg/webhook/cloneset/validating/cloneset_create_update_handler.go @@ -22,6 +22,7 @@ import ( "net/http" appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" admissionv1 "k8s.io/api/admission/v1" "k8s.io/klog/v2" @@ -76,6 +77,7 @@ func (h *CloneSetCreateUpdateHandler) Handle(ctx context.Context, req admission. } if err := deletionprotection.ValidateWorkloadDeletion(oldObj, oldObj.Spec.Replicas); err != nil { deletionprotection.WorkloadDeletionProtectionMetrics.WithLabelValues(fmt.Sprintf("%s_%s_%s", req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName()), req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName(), req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } } diff --git a/pkg/webhook/customresourcedefinition/validating/crd_handler.go b/pkg/webhook/customresourcedefinition/validating/crd_handler.go index 196c7ab0ff..59f5983e77 100644 --- a/pkg/webhook/customresourcedefinition/validating/crd_handler.go +++ b/pkg/webhook/customresourcedefinition/validating/crd_handler.go @@ -20,13 +20,13 @@ import ( "context" "net/http" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" - "k8s.io/apimachinery/pkg/runtime/schema" - admissionv1 "k8s.io/api/admission/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/runtime/inject" @@ -86,6 +86,7 @@ func (h *CRDHandler) Handle(ctx context.Context, req admission.Request) admissio if err := deletionprotection.ValidateCRDDeletion(h.Client, metaObj, gvk); err != nil { deletionprotection.CRDDeletionProtectionMetrics.WithLabelValues(metaObj.GetName(), req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, "CustomResourceDefinition", "", metaObj.GetName(), req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } return admission.ValidationResponse(true, "") diff --git a/pkg/webhook/namespace/validating/namespace_handler.go b/pkg/webhook/namespace/validating/namespace_handler.go index d2299fc04e..f3d6075960 100644 --- a/pkg/webhook/namespace/validating/namespace_handler.go +++ b/pkg/webhook/namespace/validating/namespace_handler.go @@ -20,12 +20,11 @@ import ( "context" "net/http" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" - - "k8s.io/klog/v2" - admissionv1 "k8s.io/api/admission/v1" v1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/runtime/inject" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -55,6 +54,7 @@ func (h *NamespaceHandler) Handle(ctx context.Context, req admission.Request) ad } if err := deletionprotection.ValidateNamespaceDeletion(h.Client, obj); err != nil { deletionprotection.NamespaceDeletionProtectionMetrics.WithLabelValues(obj.Name, req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, "Namespace", "", obj.Name, req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } return admission.ValidationResponse(true, "") diff --git a/pkg/webhook/statefulset/validating/statefulset_create_update_handler.go b/pkg/webhook/statefulset/validating/statefulset_create_update_handler.go index 747001acf3..a46e2e87f7 100644 --- a/pkg/webhook/statefulset/validating/statefulset_create_update_handler.go +++ b/pkg/webhook/statefulset/validating/statefulset_create_update_handler.go @@ -23,6 +23,7 @@ import ( appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1" appsv1beta1 "github.com/openkruise/kruise/apis/apps/v1beta1" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" admissionv1 "k8s.io/api/admission/v1" "k8s.io/klog/v2" @@ -87,6 +88,7 @@ func (h *StatefulSetCreateUpdateHandler) Handle(ctx context.Context, req admissi } if err := deletionprotection.ValidateWorkloadDeletion(oldObj, oldObj.Spec.Replicas); err != nil { deletionprotection.WorkloadDeletionProtectionMetrics.WithLabelValues(fmt.Sprintf("%s_%s_%s", req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName()), req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName(), req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } } diff --git a/pkg/webhook/uniteddeployment/validating/uniteddeployment_create_update_handler.go b/pkg/webhook/uniteddeployment/validating/uniteddeployment_create_update_handler.go index 4f1883a9ab..4ff427d344 100644 --- a/pkg/webhook/uniteddeployment/validating/uniteddeployment_create_update_handler.go +++ b/pkg/webhook/uniteddeployment/validating/uniteddeployment_create_update_handler.go @@ -22,6 +22,7 @@ import ( "net/http" appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1" + "github.com/openkruise/kruise/pkg/util" "github.com/openkruise/kruise/pkg/webhook/util/deletionprotection" admissionv1 "k8s.io/api/admission/v1" "k8s.io/klog/v2" @@ -78,6 +79,7 @@ func (h *UnitedDeploymentCreateUpdateHandler) Handle(ctx context.Context, req ad } if err := deletionprotection.ValidateWorkloadDeletion(oldObj, oldObj.Spec.Replicas); err != nil { deletionprotection.WorkloadDeletionProtectionMetrics.WithLabelValues(fmt.Sprintf("%s_%s_%s", req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName()), req.UserInfo.Username).Add(1) + util.LoggerProtectionInfo(util.ProtectionEventDeletionProtection, req.Kind.Kind, oldObj.GetNamespace(), oldObj.GetName(), req.UserInfo.Username) return admission.Errored(http.StatusForbidden, err) } }