include description of what kube-root-ca.crt can be used to verify

This commit is contained in:
David Eads 2021-04-08 10:23:35 -04:00
parent 11f95dc047
commit 443e4ea0df
2 changed files with 111 additions and 3 deletions

View File

@ -38,7 +38,12 @@ import (
// RootCACertConfigMapName is name of the configmap which stores certificates
// to access api-server
const RootCACertConfigMapName = "kube-root-ca.crt"
const (
RootCACertConfigMapName = "kube-root-ca.crt"
DescriptionAnnotation = "kubernetes.io/description"
Description = "Contains a CA bundle that can be used to verify the kube-apiserver when using internal endpoints such as the internal service IP or kubernetes.default.svc. " +
"No other usage is guaranteed across distributions of Kubernetes clusters."
)
func init() {
registerMetrics()
@ -186,7 +191,8 @@ func (c *Publisher) syncNamespace(ns string) (err error) {
case apierrors.IsNotFound(err):
_, err = c.client.CoreV1().ConfigMaps(ns).Create(context.TODO(), &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: RootCACertConfigMapName,
Name: RootCACertConfigMapName,
Annotations: map[string]string{DescriptionAnnotation: Description},
},
Data: map[string]string{
"ca.crt": string(c.rootCA),
@ -205,13 +211,18 @@ func (c *Publisher) syncNamespace(ns string) (err error) {
"ca.crt": string(c.rootCA),
}
if reflect.DeepEqual(cm.Data, data) {
// ensure the data and the one annotation describing usage of this configmap match.
if reflect.DeepEqual(cm.Data, data) && len(cm.Annotations[DescriptionAnnotation]) > 0 {
return nil
}
// copy so we don't modify the cache's instance of the configmap
cm = cm.DeepCopy()
cm.Data = data
if cm.Annotations == nil {
cm.Annotations = map[string]string{}
}
cm.Annotations[DescriptionAnnotation] = Description
_, err = c.client.CoreV1().ConfigMaps(ns).Update(context.TODO(), cm, metav1.UpdateOptions{})
return err

View File

@ -22,9 +22,13 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
corev1listers "k8s.io/client-go/listers/core/v1"
clienttesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/controller"
)
@ -175,3 +179,96 @@ func defaultCrtConfigMapPtr(rootCA []byte) *v1.ConfigMap {
tmp.Namespace = metav1.NamespaceDefault
return &tmp
}
func TestConfigMapUpdateNoHotLoop(t *testing.T) {
testcases := map[string]struct {
ExistingConfigMaps []runtime.Object
ExpectActions func(t *testing.T, actions []clienttesting.Action)
}{
"update-configmap-annotation": {
ExistingConfigMaps: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: RootCACertConfigMapName,
},
Data: map[string]string{"ca.crt": "fake"},
},
},
ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 1 {
t.Fatal(actions)
}
if actions[0].GetVerb() != "update" {
t.Fatal(actions)
}
actualObj := actions[0].(clienttesting.UpdateAction).GetObject()
if actualObj.(*v1.ConfigMap).Annotations[DescriptionAnnotation] != Description {
t.Fatal(actions)
}
if !reflect.DeepEqual(actualObj.(*v1.ConfigMap).Data["ca.crt"], "fake") {
t.Fatal(actions)
}
},
},
"no-update-configmap-if-annotation-present-and-equal": {
ExistingConfigMaps: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: RootCACertConfigMapName,
Annotations: map[string]string{DescriptionAnnotation: Description},
},
Data: map[string]string{"ca.crt": "fake"},
},
},
ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 0 {
t.Fatal(actions)
}
},
},
"no-update-configmap-if-annotation-present-and-different": {
ExistingConfigMaps: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: RootCACertConfigMapName,
Annotations: map[string]string{DescriptionAnnotation: "different"},
},
Data: map[string]string{"ca.crt": "fake"},
},
},
ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 0 {
t.Fatal(actions)
}
},
},
}
for k, tc := range testcases {
t.Run(k, func(t *testing.T) {
client := fake.NewSimpleClientset(tc.ExistingConfigMaps...)
configMapIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
for _, obj := range tc.ExistingConfigMaps {
configMapIndexer.Add(obj)
}
// Publisher manages certificate ConfigMap objects inside Namespaces
controller := Publisher{
client: client,
rootCA: []byte("fake"),
cmLister: corev1listers.NewConfigMapLister(configMapIndexer),
cmListerSynced: func() bool { return true },
nsListerSynced: func() bool { return true },
}
err := controller.syncNamespace("default")
if err != nil {
t.Fatal(err)
}
tc.ExpectActions(t, client.Actions())
})
}
}