@@ -3,12 +3,16 @@ package audit
33import (
44 "container/heap"
55 "context"
6+ "encoding/json"
67 "flag"
78 "fmt"
9+ "net/http"
10+ "net/http/httptest"
811 "os"
912 "path"
1013 "reflect"
1114 "strconv"
15+ "sync/atomic"
1216 "testing"
1317 "time"
1418
@@ -37,8 +41,10 @@ import (
3741 "k8s.io/apimachinery/pkg/runtime/schema"
3842 "k8s.io/apimachinery/pkg/types"
3943 "k8s.io/client-go/kubernetes/scheme"
44+ "k8s.io/client-go/rest"
4045 "sigs.k8s.io/controller-runtime/pkg/client"
4146 "sigs.k8s.io/controller-runtime/pkg/client/fake"
47+ controllerruntimemanager "sigs.k8s.io/controller-runtime/pkg/manager"
4248)
4349
4450func Test_SVQueue (t * testing.T ) {
@@ -608,6 +614,130 @@ func Test_removeAllFromDir(t *testing.T) {
608614 })
609615}
610616
617+ type auditResourcesTestManager struct {
618+ controllerruntimemanager.Manager
619+ config * rest.Config
620+ }
621+
622+ func (m * auditResourcesTestManager ) GetConfig () * rest.Config {
623+ return m .config
624+ }
625+
626+ type auditResourcesTestClient struct {
627+ client.Client
628+ cacheDir string
629+ constraintListCalls int
630+ resourceListCalls int
631+ }
632+
633+ func (c * auditResourcesTestClient ) List (_ context.Context , list client.ObjectList , _ ... client.ListOption ) error {
634+ switch list .GetObjectKind ().GroupVersionKind ().Kind {
635+ case "ConfigMapList" :
636+ c .resourceListCalls ++
637+ return nil
638+ case "K8sRequiredLabelsList" :
639+ c .constraintListCalls ++
640+ default :
641+ return fmt .Errorf ("unexpected list GVK %s" , list .GetObjectKind ().GroupVersionKind ())
642+ }
643+
644+ constraintList , ok := list .(* unstructured.UnstructuredList )
645+ if ! ok {
646+ return fmt .Errorf ("unexpected list type %T" , list )
647+ }
648+ constraintList .Items = []unstructured.Unstructured {{Object : map [string ]interface {}{
649+ "spec" : map [string ]interface {}{
650+ "match" : map [string ]interface {}{
651+ "kinds" : []interface {}{
652+ map [string ]interface {}{"kinds" : []interface {}{"Pod" }},
653+ },
654+ },
655+ },
656+ }}}
657+
658+ sentinelDir := path .Join (c .cacheDir , "sentinel" )
659+ if err := os .Mkdir (sentinelDir , 0o750 ); err != nil {
660+ return err
661+ }
662+ return os .WriteFile (path .Join (sentinelDir , auditObjectsFile ), []byte ("[]" ), 0o600 )
663+ }
664+
665+ func TestAuditResourcesSkipsUnmatchedKindBeforeCleanup (t * testing.T ) {
666+ oldAPICacheDir := * apiCacheDir
667+ oldAuditMatchKindOnly := * auditMatchKindOnly
668+ t .Cleanup (func () {
669+ * apiCacheDir = oldAPICacheDir
670+ * auditMatchKindOnly = oldAuditMatchKindOnly
671+ })
672+
673+ cacheDir := t .TempDir ()
674+ * apiCacheDir = cacheDir
675+ * auditMatchKindOnly = true
676+
677+ var coreResourcesDiscovered atomic.Bool
678+ discoveryServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
679+ w .Header ().Set ("Content-Type" , "application/json" )
680+ var response interface {}
681+ switch r .URL .Path {
682+ case "/api" :
683+ response = metav1.APIVersions {
684+ TypeMeta : metav1.TypeMeta {APIVersion : "v1" , Kind : "APIVersions" },
685+ Versions : []string {"v1" },
686+ }
687+ case "/api/v1" :
688+ coreResourcesDiscovered .Store (true )
689+ response = metav1.APIResourceList {
690+ GroupVersion : "v1" ,
691+ APIResources : []metav1.APIResource {{
692+ Name : "configmaps" ,
693+ Namespaced : true ,
694+ Kind : "ConfigMap" ,
695+ Verbs : metav1.Verbs {"list" },
696+ }},
697+ }
698+ case "/apis" :
699+ response = metav1.APIGroupList {
700+ TypeMeta : metav1.TypeMeta {APIVersion : "v1" , Kind : "APIGroupList" },
701+ }
702+ default :
703+ http .NotFound (w , r )
704+ return
705+ }
706+ if err := json .NewEncoder (w ).Encode (response ); err != nil {
707+ t .Errorf ("encoding discovery response: %v" , err )
708+ }
709+ }))
710+ t .Cleanup (discoveryServer .Close )
711+
712+ testClient := & auditResourcesTestClient {
713+ Client : fake .NewClientBuilder ().Build (),
714+ cacheDir : cacheDir ,
715+ }
716+ am := & Manager {
717+ client : testClient ,
718+ mgr : & auditResourcesTestManager {
719+ config : & rest.Config {Host : discoveryServer .URL },
720+ },
721+ log : logr .Discard (),
722+ }
723+
724+ err := am .auditResources (
725+ context .Background (),
726+ []schema.GroupVersionKind {{Group : "constraints.gatekeeper.sh" , Version : "v1beta1" , Kind : "K8sRequiredLabelsList" }},
727+ map [util.KindVersionName ]* LimitQueue {},
728+ map [util.KindVersionName ]int64 {},
729+ map [util.EnforcementAction ]int64 {},
730+ "test-timestamp" ,
731+ & auditExportPublishingState {Errors : map [string ]error {}},
732+ )
733+ require .NoError (t , err )
734+ require .True (t , coreResourcesDiscovered .Load (), "core resources were not discovered" )
735+ require .Equal (t , 1 , testClient .constraintListCalls )
736+ require .Zero (t , testClient .resourceListCalls , "resource List called for unmatched ConfigMap" )
737+ _ , err = os .Stat (path .Join (cacheDir , "sentinel" , auditObjectsFile ))
738+ require .NoError (t , err , "cache cleanup ran for unmatched ConfigMap" )
739+ }
740+
611741func Test_readUnstructured (t * testing.T ) {
612742 am := Manager {}
613743
0 commit comments