Skip to content

Commit

Permalink
Merge pull request kubevirt#5778 from ashleyschuett/fix/configmap-cab…
Browse files Browse the repository at this point in the history
…undle

update configmap cabundle if it is not parseable
  • Loading branch information
kubevirt-bot authored Jun 15, 2021
2 parents 6962cf8 + 296bc41 commit b8bc1ea
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 19 deletions.
3 changes: 3 additions & 0 deletions pkg/virt-operator/resource/apply/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//pkg/certificates/triple:go_default_library",
"//pkg/certificates/triple/cert:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/testutils:go_default_library",
"//pkg/virt-operator/resource/generate/components:go_default_library",
Expand Down Expand Up @@ -113,5 +115,6 @@ go_test(
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/util/workqueue:go_default_library",
],
)
18 changes: 12 additions & 6 deletions pkg/virt-operator/resource/apply/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (r *Reconciler) createOrUpdateComponentsWithCertificates(queue workqueue.Ra
}

// create/update CA config map
caBundle, err := r.createOrUpdateKubeVirtCAConfigMap(queue, caCert, caRenewBefore)
caBundle, err := r.createOrUpdateKubeVirtCAConfigMap(queue, caCert, caRenewBefore, findRequiredCAConfigMap(r.targetStrategy.ConfigMaps()))
if err != nil {
return err
}
Expand Down Expand Up @@ -535,17 +535,19 @@ func findRequiredCAConfigMap(configmaps []*corev1.ConfigMap) *corev1.ConfigMap {
}

func shouldUpdateBundle(required, existing *corev1.ConfigMap, key string, queue workqueue.RateLimitingInterface, caCert *tls.Certificate, overlapInterval *metav1.Duration) (bool, error) {
updateBundle := false
bundle, certCount, err := components.MergeCABundle(caCert, []byte(existing.Data[components.CABundleKey]), overlapInterval.Duration)
if err != nil {
return updateBundle, err
// the only error that can be returned form MergeCABundle is if the CA caBundle
// is unable to be parsed. If we can not parse it we should update it
return true, err
}

// ensure that we remove the old CA after the overlap period
if certCount > 1 {
queue.AddAfter(key, overlapInterval.Duration)
}

updateBundle := false
required.Data = map[string]string{components.CABundleKey: string(bundle)}
if !reflect.DeepEqual(required.Data, existing.Data) {
updateBundle = true
Expand All @@ -554,8 +556,7 @@ func shouldUpdateBundle(required, existing *corev1.ConfigMap, key string, queue
return updateBundle, nil
}

func (r *Reconciler) createOrUpdateKubeVirtCAConfigMap(queue workqueue.RateLimitingInterface, caCert *tls.Certificate, overlapInterval *metav1.Duration) (caBundle []byte, err error) {
configMap := findRequiredCAConfigMap(r.targetStrategy.ConfigMaps())
func (r *Reconciler) createOrUpdateKubeVirtCAConfigMap(queue workqueue.RateLimitingInterface, caCert *tls.Certificate, overlapInterval *metav1.Duration, configMap *corev1.ConfigMap) (caBundle []byte, err error) {
if configMap == nil {
return nil, nil
}
Expand Down Expand Up @@ -583,7 +584,12 @@ func (r *Reconciler) createOrUpdateKubeVirtCAConfigMap(queue workqueue.RateLimit
existing := obj.(*corev1.ConfigMap)
updateBundle, err := shouldUpdateBundle(configMap, existing, r.kvKey, queue, caCert, overlapInterval)
if err != nil {
return nil, err
if !updateBundle {
return nil, err
}

configMap.Data = map[string]string{components.CABundleKey: string(cert.EncodeCertPEM(caCert.Leaf))}
log.Log.Reason(err).V(2).Infof("There was an error validating the CA bundle stored in configmap %s. We are updating the bundle.", configMap.GetName())
}

modified := resourcemerge.BoolPtr(false)
Expand Down
187 changes: 174 additions & 13 deletions pkg/virt-operator/resource/apply/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@
package apply

import (
"crypto/tls"
"encoding/json"
"strings"
"time"

jsonpatch "github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/testing"
"k8s.io/client-go/util/workqueue"

"github.com/golang/mock/gomock"
"k8s.io/client-go/tools/cache"

"kubevirt.io/client-go/kubecli"
"kubevirt.io/kubevirt/pkg/certificates/triple"
"kubevirt.io/kubevirt/pkg/certificates/triple/cert"
"kubevirt.io/kubevirt/pkg/virt-operator/resource/generate/components"
"kubevirt.io/kubevirt/pkg/virt-operator/util"

. "github.com/onsi/ginkgo"
Expand Down Expand Up @@ -88,6 +94,174 @@ var _ = Describe("Apply", func() {
})
})

Context("should reconcile configmap", func() {

var clientset *kubecli.MockKubevirtClient
var ctrl *gomock.Controller
var coreclientset *fake.Clientset
var expectations *util.Expectations
var kv *v1.KubeVirt
var stores util.Stores

operatorNamespace := "opNamespace"
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
duration := &metav1.Duration{
Duration: time.Hour,
}

createCrt := func() *tls.Certificate {
caKeyPair, _ := triple.NewCA("kubevirt.io", time.Hour)

encodedCert := cert.EncodeCertPEM(caKeyPair.Cert)
encodedKey := cert.EncodePrivateKeyPEM(caKeyPair.Key)

crt, err := tls.X509KeyPair(encodedCert, encodedKey)
Expect(err).ToNot(HaveOccurred())
leaf, err := cert.ParseCertsPEM(encodedCert)
Expect(err).ToNot(HaveOccurred())
crt.Leaf = leaf[0]

return &crt
}

BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
kvInterface := kubecli.NewMockKubeVirtInterface(ctrl)
coreclientset = fake.NewSimpleClientset()

coreclientset.Fake.PrependReactor("*", "*", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
Expect(action).To(BeNil())
return true, nil, nil
})

stores = util.Stores{}
stores.ConfigMapCache = cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
stores.InstallStrategyConfigMapCache = cache.NewStore(cache.MetaNamespaceKeyFunc)

expectations = &util.Expectations{}

clientset = kubecli.NewMockKubevirtClient(ctrl)
clientset.EXPECT().KubeVirt(Namespace).Return(kvInterface).AnyTimes()
clientset.EXPECT().CoreV1().Return(coreclientset.CoreV1()).AnyTimes()

kv = &v1.KubeVirt{}
})

AfterEach(func() {
ctrl.Finish()
})

It("should not patch ConfigMap on sync", func() {
requiredCM := components.NewKubeVirtCAConfigMap(operatorNamespace)
version, imageRegistry, id := getTargetVersionRegistryID(kv)
injectOperatorMetadata(kv, &requiredCM.ObjectMeta, version, imageRegistry, id, true)

existingCM := requiredCM.DeepCopy()
crt := createCrt()

bundle, _, err := components.MergeCABundle(crt, []byte(cert.EncodeCertPEM(crt.Leaf)), time.Hour)
Expect(err).ToNot(HaveOccurred())

existingCM.Data = map[string]string{
components.CABundleKey: string(bundle),
}

stores.ConfigMapCache.Add(existingCM)

r := &Reconciler{
kv: kv,
stores: stores,
clientset: clientset,
expectations: expectations,
}

_, err = r.createOrUpdateKubeVirtCAConfigMap(queue, crt, duration, requiredCM)
Expect(err).ToNot(HaveOccurred())
})

It("should patch ConfigMap on sync when not parsable", func() {
notRSAParsableString := "something not parsable"
requiredCM := components.NewKubeVirtCAConfigMap(operatorNamespace)
version, imageRegistry, id := getTargetVersionRegistryID(kv)
injectOperatorMetadata(kv, &requiredCM.ObjectMeta, version, imageRegistry, id, true)

existingCM := requiredCM.DeepCopy()
existingCM.Data = map[string]string{
components.CABundleKey: notRSAParsableString,
}
stores.ConfigMapCache.Add(existingCM)

r := &Reconciler{
kv: kv,
stores: stores,
clientset: clientset,
expectations: expectations,
}

patched := false
coreclientset.Fake.PrependReactor("patch", "configmaps", func(action testing.Action) (handled bool, ret runtime.Object, err error) {
a := action.(testing.PatchActionImpl)
patch, err := jsonpatch.DecodePatch(a.Patch)
Expect(err).ToNot(HaveOccurred())

obj, err := json.Marshal(existingCM)
Expect(err).To(BeNil())

obj, err = patch.Apply(obj)
Expect(err).To(BeNil())

pr := &corev1.ConfigMap{}
Expect(json.Unmarshal(obj, existingCM)).To(Succeed())
Expect(existingCM.Data[components.CABundleKey]).ToNot(Equal(notRSAParsableString))

patched = true
return true, pr, nil
})

crt := createCrt()

_, err := r.createOrUpdateKubeVirtCAConfigMap(queue, crt, duration, requiredCM)
Expect(err).To(BeNil())
Expect(patched).To(BeTrue())
})

It("should patch ConfigMap on sync when CA expired", func() {
requiredCM := components.NewKubeVirtCAConfigMap(operatorNamespace)
version, imageRegistry, id := getTargetVersionRegistryID(kv)
injectOperatorMetadata(kv, &requiredCM.ObjectMeta, version, imageRegistry, id, true)

existingCM := requiredCM.DeepCopy()
crt := createCrt()

bundle, _, err := components.MergeCABundle(crt, []byte(cert.EncodeCertPEM(crt.Leaf)), time.Hour)
Expect(err).ToNot(HaveOccurred())

existingCM.Data = map[string]string{
components.CABundleKey: string(bundle),
}
stores.ConfigMapCache.Add(existingCM)

r := &Reconciler{
kv: kv,
stores: stores,
clientset: clientset,
expectations: expectations,
}

patched := false
coreclientset.Fake.PrependReactor("patch", "configmaps", func(action testing.Action) (handled bool, ret runtime.Object, err error) {
patched = true
return true, &corev1.ConfigMap{}, nil
})

updatedCrt := createCrt()

_, err = r.createOrUpdateKubeVirtCAConfigMap(queue, updatedCrt, duration, requiredCM)
Expect(err).ToNot(HaveOccurred())
Expect(patched).To(BeTrue())
})
})

Context("should reconcile service account", func() {

newServiceAccount := func() *corev1.ServiceAccount {
Expand Down Expand Up @@ -212,19 +386,6 @@ var _ = Describe("Apply", func() {
expectLabelsAnnotationsPatch bool,
expectSpecPatch bool) {

// kv := &v1.KubeVirt{
// ObjectMeta: metav1.ObjectMeta{
// Name: "test-install",
// Namespace: "default",
// Generation: int64(1),
// },
// Spec: v1.KubeVirtSpec{
// ImageTag: config.GetKubeVirtVersion(),
// ImageRegistry: config.GetImageRegistry(),
// },
// }
// config.SetTargetDeploymentConfig(kv)

Expect(hasImmutableFieldChanged(targetService, cachedService)).To(BeFalse())
ops, err := generateServicePatch(cachedService, targetService)
Expect(err).To(BeNil())
Expand Down

0 comments on commit b8bc1ea

Please sign in to comment.