mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #94050 from liggitt/remove-flaky-audit-test
Remove flaky audit e2e test
This commit is contained in:
commit
544b74c2cb
@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
|||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"audit.go",
|
|
||||||
"certificates.go",
|
"certificates.go",
|
||||||
"framework.go",
|
"framework.go",
|
||||||
"metadata_concealment.go",
|
"metadata_concealment.go",
|
||||||
@ -19,16 +18,12 @@ go_library(
|
|||||||
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
|
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
|
||||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||||
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
||||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/api/authentication/v1:go_default_library",
|
"//staging/src/k8s.io/api/authentication/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/certificates/v1:go_default_library",
|
"//staging/src/k8s.io/api/certificates/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/policy/v1beta1:go_default_library",
|
"//staging/src/k8s.io/api/policy/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
@ -37,8 +32,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1:go_default_library",
|
||||||
@ -46,7 +39,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||||
"//test/e2e/framework:go_default_library",
|
"//test/e2e/framework:go_default_library",
|
||||||
"//test/e2e/framework/auth:go_default_library",
|
"//test/e2e/framework/auth:go_default_library",
|
||||||
"//test/e2e/framework/deployment:go_default_library",
|
|
||||||
"//test/e2e/framework/job:go_default_library",
|
"//test/e2e/framework/job:go_default_library",
|
||||||
"//test/e2e/framework/kubectl:go_default_library",
|
"//test/e2e/framework/kubectl:go_default_library",
|
||||||
"//test/e2e/framework/node:go_default_library",
|
"//test/e2e/framework/node:go_default_library",
|
||||||
@ -54,7 +46,6 @@ go_library(
|
|||||||
"//test/e2e/framework/skipper:go_default_library",
|
"//test/e2e/framework/skipper:go_default_library",
|
||||||
"//test/utils:go_default_library",
|
"//test/utils:go_default_library",
|
||||||
"//test/utils/image:go_default_library",
|
"//test/utils/image:go_default_library",
|
||||||
"//vendor/github.com/evanphx/json-patch:go_default_library",
|
|
||||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||||
|
@ -1,751 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes 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 auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
||||||
apiextensionclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
|
||||||
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
|
||||||
restclient "k8s.io/client-go/rest"
|
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
|
||||||
e2eauth "k8s.io/kubernetes/test/e2e/framework/auth"
|
|
||||||
e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment"
|
|
||||||
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
|
|
||||||
"k8s.io/kubernetes/test/utils"
|
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
|
||||||
"github.com/onsi/ginkgo"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
watchTestTimeout int64 = 1
|
|
||||||
auditTestUser = "kubecfg"
|
|
||||||
|
|
||||||
crd = fixtures.NewRandomNameV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
|
|
||||||
crdName = strings.SplitN(crd.Name, ".", 2)[0]
|
|
||||||
crdNamespace = strings.SplitN(crd.Name, ".", 2)[1]
|
|
||||||
|
|
||||||
watchOptions = metav1.ListOptions{TimeoutSeconds: &watchTestTimeout}
|
|
||||||
patch, _ = json.Marshal(jsonpatch.Patch{})
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
|
|
||||||
// Marked as flaky until a reliable method for collecting server-side audit logs is available. See http://issue.k8s.io/74745#issuecomment-474052439
|
|
||||||
var _ = SIGDescribe("Advanced Audit [DisabledForLargeClusters][Flaky]", func() {
|
|
||||||
f := framework.NewDefaultFramework("audit")
|
|
||||||
var namespace string
|
|
||||||
ginkgo.BeforeEach(func() {
|
|
||||||
e2eskipper.SkipUnlessProviderIs("gce")
|
|
||||||
namespace = f.Namespace.Name
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch pods.", func() {
|
|
||||||
pod := &v1.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "audit-pod",
|
|
||||||
},
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
Containers: []v1.Container{{
|
|
||||||
Name: "pause",
|
|
||||||
Image: imageutils.GetPauseImageName(),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
updatePod := func(pod *v1.Pod) {}
|
|
||||||
|
|
||||||
f.PodClient().CreateSync(pod)
|
|
||||||
|
|
||||||
_, err := f.PodClient().Get(context.TODO(), pod.Name, metav1.GetOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to get audit-pod")
|
|
||||||
|
|
||||||
podChan, err := f.PodClient().Watch(context.TODO(), watchOptions)
|
|
||||||
framework.ExpectNoError(err, "failed to create watch for pods")
|
|
||||||
podChan.Stop()
|
|
||||||
|
|
||||||
f.PodClient().Update(pod.Name, updatePod)
|
|
||||||
|
|
||||||
_, err = f.PodClient().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to list pods")
|
|
||||||
|
|
||||||
_, err = f.PodClient().Patch(context.TODO(), pod.Name, types.JSONPatchType, patch, metav1.PatchOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to patch pod")
|
|
||||||
|
|
||||||
f.PodClient().DeleteSync(pod.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
|
||||||
Verb: "get",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
|
||||||
Verb: "list",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseStarted,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
|
||||||
Verb: "update",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
|
||||||
Verb: "patch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch deployments.", func() {
|
|
||||||
podLabels := map[string]string{"name": "audit-deployment-pod"}
|
|
||||||
d := e2edeployment.NewDeployment("audit-deployment", int32(1), podLabels, "agnhost", imageutils.GetE2EImage(imageutils.Agnhost), appsv1.RecreateDeploymentStrategyType)
|
|
||||||
|
|
||||||
_, err := f.ClientSet.AppsV1().Deployments(namespace).Create(context.TODO(), d, metav1.CreateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to create audit-deployment")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), d.Name, metav1.GetOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to get audit-deployment")
|
|
||||||
|
|
||||||
deploymentChan, err := f.ClientSet.AppsV1().Deployments(namespace).Watch(context.TODO(), watchOptions)
|
|
||||||
framework.ExpectNoError(err, "failed to create watch for deployments")
|
|
||||||
deploymentChan.Stop()
|
|
||||||
|
|
||||||
_, err = f.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), d, metav1.UpdateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to update audit-deployment")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(), d.Name, types.JSONPatchType, patch, metav1.PatchOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to patch deployment")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to create list deployments")
|
|
||||||
|
|
||||||
err = f.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), "audit-deployment", metav1.DeleteOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to delete deployments")
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments", namespace),
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
|
|
||||||
Verb: "get",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments", namespace),
|
|
||||||
Verb: "list",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseStarted,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
|
|
||||||
Verb: "update",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
|
|
||||||
Verb: "patch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "deployments",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch configmaps.", func() {
|
|
||||||
configMap := &v1.ConfigMap{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "audit-configmap",
|
|
||||||
},
|
|
||||||
Data: map[string]string{
|
|
||||||
"map-key": "map-value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := f.ClientSet.CoreV1().ConfigMaps(namespace).Create(context.TODO(), configMap, metav1.CreateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to create audit-configmap")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configMap.Name, metav1.GetOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to get audit-configmap")
|
|
||||||
|
|
||||||
configMapChan, err := f.ClientSet.CoreV1().ConfigMaps(namespace).Watch(context.TODO(), watchOptions)
|
|
||||||
framework.ExpectNoError(err, "failed to create watch for config maps")
|
|
||||||
configMapChan.Stop()
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configMap, metav1.UpdateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to update audit-configmap")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Patch(context.TODO(), configMap.Name, types.JSONPatchType, patch, metav1.PatchOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to patch configmap")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().ConfigMaps(namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to list config maps")
|
|
||||||
|
|
||||||
err = f.ClientSet.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), configMap.Name, metav1.DeleteOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to delete audit-configmap")
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
|
||||||
Verb: "get",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
|
|
||||||
Verb: "list",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseStarted,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
|
||||||
Verb: "update",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
|
||||||
Verb: "patch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "configmaps",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch secrets.", func() {
|
|
||||||
secret := &v1.Secret{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "audit-secret",
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"top-secret": []byte("foo-bar"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err := f.ClientSet.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to create audit-secret")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().Secrets(namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to get audit-secret")
|
|
||||||
|
|
||||||
secretChan, err := f.ClientSet.CoreV1().Secrets(namespace).Watch(context.TODO(), watchOptions)
|
|
||||||
framework.ExpectNoError(err, "failed to create watch for secrets")
|
|
||||||
secretChan.Stop()
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to update audit-secret")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().Secrets(namespace).Patch(context.TODO(), secret.Name, types.JSONPatchType, patch, metav1.PatchOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to patch secret")
|
|
||||||
|
|
||||||
_, err = f.ClientSet.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to list secrets")
|
|
||||||
|
|
||||||
err = f.ClientSet.CoreV1().Secrets(namespace).Delete(context.TODO(), secret.Name, metav1.DeleteOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to delete audit-secret")
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
|
||||||
Verb: "get",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
|
|
||||||
Verb: "list",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseStarted,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
|
|
||||||
Verb: "watch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
|
||||||
Verb: "update",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
|
||||||
Verb: "patch",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "secrets",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should audit API calls to create and delete custom resource definition.", func() {
|
|
||||||
config, err := framework.LoadConfig()
|
|
||||||
framework.ExpectNoError(err, "failed to load config")
|
|
||||||
apiExtensionClient, err := apiextensionclientset.NewForConfig(config)
|
|
||||||
framework.ExpectNoError(err, "failed to initialize apiExtensionClient")
|
|
||||||
|
|
||||||
crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, f.DynamicClient)
|
|
||||||
framework.ExpectNoError(err, "failed to create custom resource definition")
|
|
||||||
err = fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionClient)
|
|
||||||
framework.ExpectNoError(err, "failed to delete custom resource definition")
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "customresourcedefinitions",
|
|
||||||
RequestObject: true,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/%s/v1beta1/%s", crdNamespace, crdName),
|
|
||||||
Verb: "create",
|
|
||||||
Code: 201,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: crdName,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelRequestResponse,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/%s", crd.Name),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: "customresourcedefinitions",
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: true,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
}, {
|
|
||||||
Level: auditinternal.LevelMetadata,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/apis/%s/v1beta1/%s/setup-instance", crdNamespace, crdName),
|
|
||||||
Verb: "delete",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
Resource: crdName,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// test authorizer annotations, RBAC is required.
|
|
||||||
ginkgo.It("should audit API calls to get a pod with unauthorized user.", func() {
|
|
||||||
if !e2eauth.IsRBACEnabled(f.ClientSet.RbacV1()) {
|
|
||||||
e2eskipper.Skipf("RBAC not enabled.")
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("Creating a kubernetes client that impersonates an unauthorized anonymous user")
|
|
||||||
config, err := framework.LoadConfig()
|
|
||||||
framework.ExpectNoError(err)
|
|
||||||
config.Impersonate = restclient.ImpersonationConfig{
|
|
||||||
UserName: "system:anonymous",
|
|
||||||
Groups: []string{"system:unauthenticated"},
|
|
||||||
}
|
|
||||||
anonymousClient, err := clientset.NewForConfig(config)
|
|
||||||
framework.ExpectNoError(err)
|
|
||||||
|
|
||||||
_, err = anonymousClient.CoreV1().Pods(namespace).Get(context.TODO(), "another-audit-pod", metav1.GetOptions{})
|
|
||||||
expectForbidden(err)
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/another-audit-pod", namespace),
|
|
||||||
Verb: "get",
|
|
||||||
Code: 403,
|
|
||||||
User: auditTestUser,
|
|
||||||
ImpersonatedUser: "system:anonymous",
|
|
||||||
ImpersonatedGroups: "system:unauthenticated",
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "forbid",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should list pods as impersonated user.", func() {
|
|
||||||
ginkgo.By("Creating a kubernetes client that impersonates an authorized user")
|
|
||||||
config, err := framework.LoadConfig()
|
|
||||||
framework.ExpectNoError(err)
|
|
||||||
config.Impersonate = restclient.ImpersonationConfig{
|
|
||||||
UserName: "superman",
|
|
||||||
Groups: []string{"system:masters"},
|
|
||||||
}
|
|
||||||
impersonatedClient, err := clientset.NewForConfig(config)
|
|
||||||
framework.ExpectNoError(err)
|
|
||||||
|
|
||||||
_, err = impersonatedClient.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
framework.ExpectNoError(err, "failed to list pods")
|
|
||||||
|
|
||||||
expectEvents(f, []utils.AuditEvent{
|
|
||||||
{
|
|
||||||
Level: auditinternal.LevelRequest,
|
|
||||||
Stage: auditinternal.StageResponseComplete,
|
|
||||||
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
|
||||||
Verb: "list",
|
|
||||||
Code: 200,
|
|
||||||
User: auditTestUser,
|
|
||||||
ImpersonatedUser: "superman",
|
|
||||||
ImpersonatedGroups: "system:masters",
|
|
||||||
Resource: "pods",
|
|
||||||
Namespace: namespace,
|
|
||||||
RequestObject: false,
|
|
||||||
ResponseObject: false,
|
|
||||||
AuthorizeDecision: "allow",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
func expectEvents(f *framework.Framework, expectedEvents []utils.AuditEvent) {
|
|
||||||
// The default flush timeout is 30 seconds, therefore it should be enough to retry once
|
|
||||||
// to find all expected events. However, we're waiting for 5 minutes to avoid flakes.
|
|
||||||
pollingInterval := 30 * time.Second
|
|
||||||
pollingTimeout := 5 * time.Minute
|
|
||||||
err := wait.Poll(pollingInterval, pollingTimeout, func() (bool, error) {
|
|
||||||
// Fetch the log stream.
|
|
||||||
stream, err := f.ClientSet.CoreV1().RESTClient().Get().AbsPath("/logs/kube-apiserver-audit.log").Stream(context.TODO())
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer stream.Close()
|
|
||||||
missingReport, err := utils.CheckAuditLines(stream, expectedEvents, auditv1.SchemeGroupVersion)
|
|
||||||
if err != nil {
|
|
||||||
framework.Logf("Failed to observe audit events: %v", err)
|
|
||||||
} else if len(missingReport.MissingEvents) > 0 {
|
|
||||||
framework.Logf(missingReport.String())
|
|
||||||
}
|
|
||||||
return len(missingReport.MissingEvents) == 0, nil
|
|
||||||
})
|
|
||||||
framework.ExpectNoError(err, "after %v failed to observe audit events", pollingTimeout)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user