k8s.io/apiserver@v0.31.1/pkg/admission/plugin/policy/generic/policy_source_test.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package generic_test
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  	"k8s.io/api/admissionregistration/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/types"
    27  	"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
    28  	"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
    29  	"k8s.io/apiserver/pkg/authorization/authorizer"
    30  	"k8s.io/client-go/tools/cache"
    31  )
    32  
    33  func makeTestDispatcher(authorizer.Authorizer, *matching.Matcher) generic.Dispatcher[generic.PolicyHook[*FakePolicy, *FakeBinding, generic.Evaluator]] {
    34  	return nil
    35  }
    36  
    37  func TestPolicySourceHasSyncedEmpty(t *testing.T) {
    38  	testContext, testCancel, err := generic.NewPolicyTestContext(
    39  		func(fp *FakePolicy) generic.PolicyAccessor { return fp },
    40  		func(fb *FakeBinding) generic.BindingAccessor { return fb },
    41  		func(fp *FakePolicy) generic.Evaluator { return nil },
    42  		makeTestDispatcher,
    43  		nil,
    44  		nil,
    45  	)
    46  	require.NoError(t, err)
    47  	defer testCancel()
    48  	require.NoError(t, testContext.Start())
    49  
    50  	// Should be able to wait for cache sync
    51  	require.True(t, cache.WaitForCacheSync(testContext.Done(), testContext.Source.HasSynced), "cache should sync after informer running")
    52  }
    53  
    54  func TestPolicySourceHasSyncedInitialList(t *testing.T) {
    55  	// Create a list of fake policies
    56  	initialObjects := []runtime.Object{
    57  		&FakePolicy{
    58  			ObjectMeta: metav1.ObjectMeta{
    59  				Name: "policy1",
    60  			},
    61  		},
    62  		&FakeBinding{
    63  			ObjectMeta: metav1.ObjectMeta{
    64  				Name: "binding1",
    65  			},
    66  			PolicyName: "policy1",
    67  		},
    68  	}
    69  
    70  	testContext, testCancel, err := generic.NewPolicyTestContext(
    71  		func(fp *FakePolicy) generic.PolicyAccessor { return fp },
    72  		func(fb *FakeBinding) generic.BindingAccessor { return fb },
    73  		func(fp *FakePolicy) generic.Evaluator { return nil },
    74  		makeTestDispatcher,
    75  		initialObjects,
    76  		nil,
    77  	)
    78  	require.NoError(t, err)
    79  	defer testCancel()
    80  	require.NoError(t, testContext.Start())
    81  	// Should be able to wait for cache sync
    82  	require.Len(t, testContext.Source.Hooks(), 1, "should have one policy")
    83  	require.NoError(t, testContext.UpdateAndWait(
    84  		&FakePolicy{
    85  			ObjectMeta: metav1.ObjectMeta{
    86  				Name: "policy2",
    87  			},
    88  		},
    89  		&FakeBinding{
    90  			ObjectMeta: metav1.ObjectMeta{
    91  				Name: "binding2",
    92  			},
    93  			PolicyName: "policy2",
    94  		},
    95  	))
    96  	require.Len(t, testContext.Source.Hooks(), 2, "should have two policies")
    97  	require.NoError(t, testContext.UpdateAndWait(
    98  		&FakePolicy{
    99  			ObjectMeta: metav1.ObjectMeta{
   100  				Name: "policy3",
   101  			},
   102  		},
   103  		&FakeBinding{
   104  			ObjectMeta: metav1.ObjectMeta{
   105  				Name: "binding3",
   106  			},
   107  			PolicyName: "policy3",
   108  		},
   109  		&FakePolicy{
   110  			ObjectMeta: metav1.ObjectMeta{
   111  				Name: "policy2",
   112  			},
   113  			ParamKind: &v1.ParamKind{
   114  				APIVersion: "policy.example.com/v1",
   115  				Kind:       "FakeParam",
   116  			},
   117  		},
   118  	))
   119  	require.Len(t, testContext.Source.Hooks(), 3, "should have 3 policies")
   120  
   121  }
   122  
   123  func TestPolicySourceBindsToPolicies(t *testing.T) {
   124  	// Create a list of fake policies
   125  	initialObjects := []runtime.Object{
   126  		&FakePolicy{
   127  			ObjectMeta: metav1.ObjectMeta{
   128  				Name: "policy1",
   129  			},
   130  		},
   131  		&FakeBinding{
   132  			ObjectMeta: metav1.ObjectMeta{
   133  				Name: "binding1",
   134  			},
   135  			PolicyName: "policy1",
   136  		},
   137  	}
   138  
   139  	testContext, testCancel, err := generic.NewPolicyTestContext(
   140  		func(fp *FakePolicy) generic.PolicyAccessor { return fp },
   141  		func(fb *FakeBinding) generic.BindingAccessor { return fb },
   142  		func(fp *FakePolicy) generic.Evaluator { return nil },
   143  		makeTestDispatcher,
   144  		initialObjects,
   145  		nil,
   146  	)
   147  	require.NoError(t, err)
   148  	defer testCancel()
   149  	require.NoError(t, testContext.Start())
   150  
   151  	require.Len(t, testContext.Source.Hooks(), 1, "should have one policy")
   152  	require.Len(t, testContext.Source.Hooks()[0].Bindings, 1, "should have one binding")
   153  	require.Equal(t, "binding1", testContext.Source.Hooks()[0].Bindings[0].GetName(), "should have one binding")
   154  
   155  	// Change the binding to another policy (policies without bindings should
   156  	// be ignored, so it should remove the first
   157  	require.NoError(t, testContext.UpdateAndWait(
   158  		&FakePolicy{
   159  			ObjectMeta: metav1.ObjectMeta{
   160  				Name: "policy2",
   161  			},
   162  		},
   163  		&FakeBinding{
   164  			ObjectMeta: metav1.ObjectMeta{
   165  				Name: "binding1",
   166  			},
   167  			PolicyName: "policy2",
   168  		}))
   169  	require.Len(t, testContext.Source.Hooks(), 1, "should have one policy")
   170  	require.Equal(t, "policy2", testContext.Source.Hooks()[0].Policy.GetName(), "policy name should be policy2")
   171  	require.Len(t, testContext.Source.Hooks()[0].Bindings, 1, "should have one binding")
   172  	require.Equal(t, "binding1", testContext.Source.Hooks()[0].Bindings[0].GetName(), "binding name should be binding1")
   173  
   174  }
   175  
   176  type FakePolicy struct {
   177  	metav1.TypeMeta
   178  	metav1.ObjectMeta
   179  
   180  	ParamKind *v1.ParamKind
   181  }
   182  
   183  var _ generic.PolicyAccessor = &FakePolicy{}
   184  
   185  type FakeBinding struct {
   186  	metav1.TypeMeta
   187  	metav1.ObjectMeta
   188  
   189  	PolicyName string
   190  }
   191  
   192  var _ generic.BindingAccessor = &FakeBinding{}
   193  
   194  func (fp *FakePolicy) GetName() string {
   195  	return fp.Name
   196  }
   197  
   198  func (fp *FakePolicy) GetNamespace() string {
   199  	return fp.Namespace
   200  }
   201  
   202  func (fp *FakePolicy) GetParamKind() *v1.ParamKind {
   203  	return fp.ParamKind
   204  }
   205  
   206  func (fb *FakePolicy) GetMatchConstraints() *v1.MatchResources {
   207  	return nil
   208  }
   209  
   210  func (fb *FakeBinding) GetName() string {
   211  	return fb.Name
   212  }
   213  
   214  func (fb *FakeBinding) GetNamespace() string {
   215  	return fb.Namespace
   216  }
   217  
   218  func (fb *FakeBinding) GetPolicyName() types.NamespacedName {
   219  	return types.NamespacedName{
   220  		Name: fb.PolicyName,
   221  	}
   222  }
   223  
   224  func (fb *FakeBinding) GetMatchResources() *v1.MatchResources {
   225  	return nil
   226  }
   227  
   228  func (fb *FakeBinding) GetParamRef() *v1.ParamRef {
   229  	return nil
   230  }
   231  
   232  func (fp *FakePolicy) DeepCopyObject() runtime.Object {
   233  	// totally fudged deepcopy
   234  	newFP := &FakePolicy{}
   235  	*newFP = *fp
   236  	return newFP
   237  }
   238  
   239  func (fb *FakeBinding) DeepCopyObject() runtime.Object {
   240  	// totally fudged deepcopy
   241  	newFB := &FakeBinding{}
   242  	*newFB = *fb
   243  	return newFB
   244  }