...

Source file src/k8s.io/kubernetes/pkg/controller/certificates/rootcacertpublisher/publisher_test.go

Documentation: k8s.io/kubernetes/pkg/controller/certificates/rootcacertpublisher

     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 rootcacertpublisher
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/client-go/informers"
    29  	"k8s.io/client-go/kubernetes/fake"
    30  	corev1listers "k8s.io/client-go/listers/core/v1"
    31  	clienttesting "k8s.io/client-go/testing"
    32  	"k8s.io/client-go/tools/cache"
    33  	"k8s.io/kubernetes/pkg/controller"
    34  )
    35  
    36  func TestConfigMapCreation(t *testing.T) {
    37  	ns := metav1.NamespaceDefault
    38  	fakeRootCA := []byte("fake-root-ca")
    39  
    40  	caConfigMap := defaultCrtConfigMapPtr(fakeRootCA)
    41  	addFieldCM := defaultCrtConfigMapPtr(fakeRootCA)
    42  	addFieldCM.Data["test"] = "test"
    43  	modifyFieldCM := defaultCrtConfigMapPtr([]byte("abc"))
    44  	otherConfigMap := &v1.ConfigMap{
    45  		ObjectMeta: metav1.ObjectMeta{
    46  			Name:            "other",
    47  			Namespace:       ns,
    48  			ResourceVersion: "1",
    49  		},
    50  	}
    51  	updateOtherConfigMap := &v1.ConfigMap{
    52  		ObjectMeta: metav1.ObjectMeta{
    53  			Name:            "other",
    54  			Namespace:       ns,
    55  			ResourceVersion: "1",
    56  		},
    57  		Data: map[string]string{"aa": "bb"},
    58  	}
    59  
    60  	existNS := &v1.Namespace{
    61  		ObjectMeta: metav1.ObjectMeta{Name: ns},
    62  		Status: v1.NamespaceStatus{
    63  			Phase: v1.NamespaceActive,
    64  		},
    65  	}
    66  	newNs := &v1.Namespace{
    67  		ObjectMeta: metav1.ObjectMeta{Name: "new"},
    68  		Status: v1.NamespaceStatus{
    69  			Phase: v1.NamespaceActive,
    70  		},
    71  	}
    72  	terminatingNS := &v1.Namespace{
    73  		ObjectMeta: metav1.ObjectMeta{Name: ns},
    74  		Status: v1.NamespaceStatus{
    75  			Phase: v1.NamespaceTerminating,
    76  		},
    77  	}
    78  
    79  	type action struct {
    80  		verb string
    81  		name string
    82  	}
    83  	testcases := map[string]struct {
    84  		ExistingConfigMaps []*v1.ConfigMap
    85  		AddedNamespace     *v1.Namespace
    86  		UpdatedNamespace   *v1.Namespace
    87  		DeletedConfigMap   *v1.ConfigMap
    88  		UpdatedConfigMap   *v1.ConfigMap
    89  		ExpectActions      []action
    90  	}{
    91  		"create new namespace": {
    92  			AddedNamespace: newNs,
    93  			ExpectActions:  []action{{verb: "create", name: RootCACertConfigMapName}},
    94  		},
    95  		"delete other configmap": {
    96  			ExistingConfigMaps: []*v1.ConfigMap{otherConfigMap, caConfigMap},
    97  			DeletedConfigMap:   otherConfigMap,
    98  		},
    99  		"delete ca configmap": {
   100  			ExistingConfigMaps: []*v1.ConfigMap{otherConfigMap, caConfigMap},
   101  			DeletedConfigMap:   caConfigMap,
   102  			ExpectActions:      []action{{verb: "create", name: RootCACertConfigMapName}},
   103  		},
   104  		"update ca configmap with adding field": {
   105  			ExistingConfigMaps: []*v1.ConfigMap{caConfigMap},
   106  			UpdatedConfigMap:   addFieldCM,
   107  			ExpectActions:      []action{{verb: "update", name: RootCACertConfigMapName}},
   108  		},
   109  		"update ca configmap with modifying field": {
   110  			ExistingConfigMaps: []*v1.ConfigMap{caConfigMap},
   111  			UpdatedConfigMap:   modifyFieldCM,
   112  			ExpectActions:      []action{{verb: "update", name: RootCACertConfigMapName}},
   113  		},
   114  		"update with other configmap": {
   115  			ExistingConfigMaps: []*v1.ConfigMap{caConfigMap, otherConfigMap},
   116  			UpdatedConfigMap:   updateOtherConfigMap,
   117  		},
   118  		"update namespace with terminating state": {
   119  			UpdatedNamespace: terminatingNS,
   120  		},
   121  	}
   122  
   123  	for k, tc := range testcases {
   124  		t.Run(k, func(t *testing.T) {
   125  			client := fake.NewSimpleClientset(caConfigMap, existNS)
   126  			informers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(), controller.NoResyncPeriodFunc())
   127  			cmInformer := informers.Core().V1().ConfigMaps()
   128  			nsInformer := informers.Core().V1().Namespaces()
   129  			controller, err := NewPublisher(cmInformer, nsInformer, client, fakeRootCA)
   130  			if err != nil {
   131  				t.Fatalf("error creating ServiceAccounts controller: %v", err)
   132  			}
   133  
   134  			cmStore := cmInformer.Informer().GetStore()
   135  
   136  			controller.syncHandler = controller.syncNamespace
   137  
   138  			for _, s := range tc.ExistingConfigMaps {
   139  				cmStore.Add(s)
   140  			}
   141  
   142  			if tc.AddedNamespace != nil {
   143  				controller.namespaceAdded(tc.AddedNamespace)
   144  			}
   145  			if tc.UpdatedNamespace != nil {
   146  				controller.namespaceUpdated(nil, tc.UpdatedNamespace)
   147  			}
   148  
   149  			if tc.DeletedConfigMap != nil {
   150  				cmStore.Delete(tc.DeletedConfigMap)
   151  				controller.configMapDeleted(tc.DeletedConfigMap)
   152  			}
   153  
   154  			if tc.UpdatedConfigMap != nil {
   155  				cmStore.Add(tc.UpdatedConfigMap)
   156  				controller.configMapUpdated(nil, tc.UpdatedConfigMap)
   157  			}
   158  			ctx := context.TODO()
   159  			for controller.queue.Len() != 0 {
   160  				controller.processNextWorkItem(ctx)
   161  			}
   162  
   163  			actions := client.Actions()
   164  			if reflect.DeepEqual(actions, tc.ExpectActions) {
   165  				t.Errorf("Unexpected actions:\n%s", cmp.Diff(actions, tc.ExpectActions))
   166  			}
   167  		})
   168  	}
   169  }
   170  
   171  func defaultCrtConfigMapPtr(rootCA []byte) *v1.ConfigMap {
   172  	tmp := v1.ConfigMap{
   173  		ObjectMeta: metav1.ObjectMeta{
   174  			Name: RootCACertConfigMapName,
   175  		},
   176  		Data: map[string]string{
   177  			"ca.crt": string(rootCA),
   178  		},
   179  	}
   180  	tmp.Namespace = metav1.NamespaceDefault
   181  	return &tmp
   182  }
   183  
   184  func TestConfigMapUpdateNoHotLoop(t *testing.T) {
   185  	testcases := map[string]struct {
   186  		ExistingConfigMaps []runtime.Object
   187  		ExpectActions      func(t *testing.T, actions []clienttesting.Action)
   188  	}{
   189  		"update-configmap-annotation": {
   190  			ExistingConfigMaps: []runtime.Object{
   191  				&v1.ConfigMap{
   192  					ObjectMeta: metav1.ObjectMeta{
   193  						Namespace: "default",
   194  						Name:      RootCACertConfigMapName,
   195  					},
   196  					Data: map[string]string{"ca.crt": "fake"},
   197  				},
   198  			},
   199  			ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
   200  				if len(actions) != 1 {
   201  					t.Fatal(actions)
   202  				}
   203  				if actions[0].GetVerb() != "update" {
   204  					t.Fatal(actions)
   205  				}
   206  				actualObj := actions[0].(clienttesting.UpdateAction).GetObject()
   207  				if actualObj.(*v1.ConfigMap).Annotations[DescriptionAnnotation] != Description {
   208  					t.Fatal(actions)
   209  				}
   210  				if !reflect.DeepEqual(actualObj.(*v1.ConfigMap).Data["ca.crt"], "fake") {
   211  					t.Fatal(actions)
   212  				}
   213  			},
   214  		},
   215  		"no-update-configmap-if-annotation-present-and-equal": {
   216  			ExistingConfigMaps: []runtime.Object{
   217  				&v1.ConfigMap{
   218  					ObjectMeta: metav1.ObjectMeta{
   219  						Namespace:   "default",
   220  						Name:        RootCACertConfigMapName,
   221  						Annotations: map[string]string{DescriptionAnnotation: Description},
   222  					},
   223  					Data: map[string]string{"ca.crt": "fake"},
   224  				},
   225  			},
   226  			ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
   227  				if len(actions) != 0 {
   228  					t.Fatal(actions)
   229  				}
   230  			},
   231  		},
   232  		"no-update-configmap-if-annotation-present-and-different": {
   233  			ExistingConfigMaps: []runtime.Object{
   234  				&v1.ConfigMap{
   235  					ObjectMeta: metav1.ObjectMeta{
   236  						Namespace:   "default",
   237  						Name:        RootCACertConfigMapName,
   238  						Annotations: map[string]string{DescriptionAnnotation: "different"},
   239  					},
   240  					Data: map[string]string{"ca.crt": "fake"},
   241  				},
   242  			},
   243  			ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
   244  				if len(actions) != 0 {
   245  					t.Fatal(actions)
   246  				}
   247  			},
   248  		},
   249  	}
   250  
   251  	for k, tc := range testcases {
   252  		t.Run(k, func(t *testing.T) {
   253  			client := fake.NewSimpleClientset(tc.ExistingConfigMaps...)
   254  			configMapIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
   255  			for _, obj := range tc.ExistingConfigMaps {
   256  				configMapIndexer.Add(obj)
   257  			}
   258  
   259  			// Publisher manages certificate ConfigMap objects inside Namespaces
   260  			controller := Publisher{
   261  				client:         client,
   262  				rootCA:         []byte("fake"),
   263  				cmLister:       corev1listers.NewConfigMapLister(configMapIndexer),
   264  				cmListerSynced: func() bool { return true },
   265  				nsListerSynced: func() bool { return true },
   266  			}
   267  			ctx := context.TODO()
   268  			err := controller.syncNamespace(ctx, "default")
   269  			if err != nil {
   270  				t.Fatal(err)
   271  			}
   272  			tc.ExpectActions(t, client.Actions())
   273  		})
   274  	}
   275  }
   276  

View as plain text