k8s.io/apiserver@v0.31.1/pkg/admission/audit_test.go (about)

     1  /*
     2  Copyright 2018 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 admission
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  	"testing"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    27  	"k8s.io/apiserver/pkg/audit"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  // fakeHandler implements Interface
    34  type fakeHandler struct {
    35  	// return value of Admit()
    36  	admit error
    37  	// annotations add to attributesRecord during Admit() phase
    38  	admitAnnotations map[string]string
    39  	// return value of Validate()
    40  	validate error
    41  	// annotations add to attributesRecord during Validate() phase
    42  	validateAnnotations map[string]string
    43  	// return value of Handles()
    44  	handles bool
    45  }
    46  
    47  var _ Interface = &fakeHandler{}
    48  var _ MutationInterface = &fakeHandler{}
    49  var _ ValidationInterface = &fakeHandler{}
    50  
    51  func (h fakeHandler) Admit(ctx context.Context, a Attributes, o ObjectInterfaces) error {
    52  	for k, v := range h.admitAnnotations {
    53  		a.AddAnnotation(k, v)
    54  	}
    55  	return h.admit
    56  }
    57  
    58  func (h fakeHandler) Validate(ctx context.Context, a Attributes, o ObjectInterfaces) error {
    59  	for k, v := range h.validateAnnotations {
    60  		a.AddAnnotation(k, v)
    61  	}
    62  	return h.validate
    63  }
    64  
    65  func (h fakeHandler) Handles(o Operation) bool {
    66  	return h.handles
    67  }
    68  
    69  func attributes() Attributes {
    70  	return NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "", schema.GroupVersionResource{}, "", "", nil, false, nil)
    71  }
    72  
    73  func TestWithAudit(t *testing.T) {
    74  	var testCases = map[string]struct {
    75  		admit               error
    76  		admitAnnotations    map[string]string
    77  		validate            error
    78  		validateAnnotations map[string]string
    79  		handles             bool
    80  	}{
    81  		"not handle": {
    82  			nil,
    83  			nil,
    84  			nil,
    85  			nil,
    86  			false,
    87  		},
    88  		"allow": {
    89  			nil,
    90  			nil,
    91  			nil,
    92  			nil,
    93  			true,
    94  		},
    95  		"allow with annotations": {
    96  			nil,
    97  			map[string]string{
    98  				"plugin.example.com/foo": "bar",
    99  			},
   100  			nil,
   101  			nil,
   102  			true,
   103  		},
   104  		"allow with annotations overwrite": {
   105  			nil,
   106  			map[string]string{
   107  				"plugin.example.com/foo": "bar",
   108  			},
   109  			nil,
   110  			map[string]string{
   111  				"plugin.example.com/foo": "bar",
   112  			},
   113  			true,
   114  		},
   115  		"forbidden error": {
   116  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   117  			nil,
   118  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   119  			nil,
   120  			true,
   121  		},
   122  		"forbidden error with annotations": {
   123  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   124  			nil,
   125  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   126  			map[string]string{
   127  				"plugin.example.com/foo": "bar",
   128  			},
   129  			true,
   130  		},
   131  		"forbidden error with annotations overwrite": {
   132  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   133  			map[string]string{
   134  				"plugin.example.com/foo": "bar",
   135  			},
   136  			NewForbidden(attributes(), fmt.Errorf("quota exceeded")),
   137  			map[string]string{
   138  				"plugin.example.com/foo": "bar",
   139  			},
   140  			true,
   141  		},
   142  	}
   143  	for tcName, tc := range testCases {
   144  		var handler Interface = fakeHandler{tc.admit, tc.admitAnnotations, tc.validate, tc.validateAnnotations, tc.handles}
   145  		ctx := audit.WithAuditContext(context.Background())
   146  		ac := audit.AuditContextFrom(ctx)
   147  		ae := &ac.Event
   148  		ae.Level = auditinternal.LevelMetadata
   149  		auditHandler := WithAudit(handler)
   150  		a := attributes()
   151  
   152  		assert.Equal(t, handler.Handles(Create), auditHandler.Handles(Create), tcName+": WithAudit decorator should not effect the return value")
   153  
   154  		mutator, ok := handler.(MutationInterface)
   155  		require.True(t, ok)
   156  		auditMutator, ok := auditHandler.(MutationInterface)
   157  		require.True(t, ok)
   158  		assert.Equal(t, mutator.Admit(ctx, a, nil), auditMutator.Admit(ctx, a, nil), tcName+": WithAudit decorator should not effect the return value")
   159  
   160  		validator, ok := handler.(ValidationInterface)
   161  		require.True(t, ok)
   162  		auditValidator, ok := auditHandler.(ValidationInterface)
   163  		require.True(t, ok)
   164  		assert.Equal(t, validator.Validate(ctx, a, nil), auditValidator.Validate(ctx, a, nil), tcName+": WithAudit decorator should not effect the return value")
   165  
   166  		annotations := make(map[string]string, len(tc.admitAnnotations)+len(tc.validateAnnotations))
   167  		for k, v := range tc.admitAnnotations {
   168  			annotations[k] = v
   169  		}
   170  		for k, v := range tc.validateAnnotations {
   171  			annotations[k] = v
   172  		}
   173  		if len(annotations) == 0 {
   174  			assert.Nil(t, ae.Annotations, tcName+": unexptected annotations set in audit event")
   175  		} else {
   176  			assert.Equal(t, annotations, ae.Annotations, tcName+": unexptected annotations set in audit event")
   177  		}
   178  	}
   179  }
   180  
   181  func TestWithAuditConcurrency(t *testing.T) {
   182  	admitAnnotations := map[string]string{
   183  		"plugin.example.com/foo": "foo",
   184  		"plugin.example.com/bar": "bar",
   185  		"plugin.example.com/baz": "baz",
   186  		"plugin.example.com/qux": "qux",
   187  	}
   188  	var handler Interface = fakeHandler{admitAnnotations: admitAnnotations, handles: true}
   189  	ctx := audit.WithAuditContext(context.Background())
   190  	ac := audit.AuditContextFrom(ctx)
   191  	ac.Event.Level = auditinternal.LevelMetadata
   192  	auditHandler := WithAudit(handler)
   193  	a := attributes()
   194  
   195  	// Simulate the scenario store.DeleteCollection
   196  	workers := 2
   197  	wg := &sync.WaitGroup{}
   198  	wg.Add(workers)
   199  	for i := 0; i < workers; i++ {
   200  		go func() {
   201  			defer wg.Done()
   202  			mutator, ok := handler.(MutationInterface)
   203  			require.True(t, ok)
   204  			auditMutator, ok := auditHandler.(MutationInterface)
   205  			require.True(t, ok)
   206  			assert.Equal(t, mutator.Admit(ctx, a, nil), auditMutator.Admit(ctx, a, nil), "WithAudit decorator should not effect the return value")
   207  		}()
   208  	}
   209  	wg.Wait()
   210  }