Skip to content

Commit

Permalink
Fix trust bundle propagation (#7924)
Browse files Browse the repository at this point in the history
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
  • Loading branch information
pierDipi committed May 21, 2024
1 parent 15be248 commit 5355171
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 77 deletions.
73 changes: 22 additions & 51 deletions pkg/eventingtls/trust_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,22 @@ func PropagateTrustBundles(ctx context.Context, k8s kubernetes.Interface, trustB
for _, cm := range systemNamespaceBundles {
name := userCMName(cm.Name)
if p, ok := state[name]; !ok {
state[name] = Pair{sysCM: cm}
state[name] = Pair{sysCM: cm.DeepCopy()}
} else {
state[name] = Pair{
sysCM: cm,
sysCM: cm.DeepCopy(),
userCm: p.userCm,
}
}
}

for _, cm := range userNamespaceBundles {
if p, ok := state[cm.Name]; !ok {
state[cm.Name] = Pair{userCm: cm}
state[cm.Name] = Pair{userCm: cm.DeepCopy()}
} else {
state[cm.Name] = Pair{
sysCM: p.sysCM,
userCm: cm,
userCm: cm.DeepCopy(),
}
}
}
Expand All @@ -107,26 +107,26 @@ func PropagateTrustBundles(ctx context.Context, k8s kubernetes.Interface, trustB
APIVersion: gvk.GroupVersion().String(),
Kind: gvk.Kind,
Name: obj.GetName(),
UID: obj.GetUID(),
}

for _, or := range p.userCm.OwnerReferences {
// Only delete the ConfigMap if the object owns it
if equality.Semantic.DeepDerivative(expectedOr, or) {
if err := deleteConfigMap(ctx, k8s, obj, p.userCm); err != nil {
return err
}
}
}

continue
}

expected := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: userCMName(p.sysCM.Name),
Namespace: obj.GetNamespace(),
Labels: map[string]string{
TrustBundleLabelKey: TrustBundleLabelValue,
},
Name: userCMName(p.sysCM.Name),
Namespace: obj.GetNamespace(),
Labels: p.sysCM.Labels,
Annotations: p.sysCM.Annotations,
},
Data: p.sysCM.Data,
BinaryData: p.sysCM.BinaryData,
Expand All @@ -135,32 +135,14 @@ func PropagateTrustBundles(ctx context.Context, k8s kubernetes.Interface, trustB
if p.userCm == nil {
// Update owner references
expected.OwnerReferences = withOwnerReferences(obj, gvk, []metav1.OwnerReference{})
err := createConfigMap(ctx, k8s, expected)
if err != nil && !apierrors.IsAlreadyExists(err) {
if err := createConfigMap(ctx, k8s, expected); err != nil {
return err
}
if apierrors.IsAlreadyExists(err) {
// Update existing ConfigMap
cm, getErr := k8s.CoreV1().
ConfigMaps(obj.GetNamespace()).
Get(ctx, userCMName(p.sysCM.Name), metav1.GetOptions{})
if getErr != nil {
return err // return original error
}

cm.ObjectMeta.DeepCopyInto(&expected.ObjectMeta)
expected.OwnerReferences = withOwnerReferences(obj, gvk, cm.OwnerReferences)

if !equality.Semantic.DeepDerivative(expected, cm) {
if err := updateConfigMap(ctx, k8s, expected); err != nil {
return err
}
}
}
continue
}

p.userCm.ObjectMeta.DeepCopyInto(&expected.ObjectMeta)
expected.Generation = p.userCm.Generation
expected.ResourceVersion = p.userCm.ResourceVersion
// Update owner references
expected.OwnerReferences = withOwnerReferences(obj, gvk, p.userCm.OwnerReferences)

Expand Down Expand Up @@ -277,26 +259,15 @@ func withOwnerReferences(sb kmeta.Accessor, gvk schema.GroupVersionKind, referen
}

func deleteConfigMap(ctx context.Context, k8s kubernetes.Interface, sb kmeta.Accessor, cm *corev1.ConfigMap) error {
expectedOr := metav1.OwnerReference{
APIVersion: sb.GroupVersionKind().GroupVersion().String(),
Kind: sb.GroupVersionKind().Kind,
Name: sb.GetName(),
}
// Only delete the ConfigMap if the object owns it
for _, or := range cm.OwnerReferences {
if equality.Semantic.DeepDerivative(expectedOr, or) {
err := k8s.CoreV1().ConfigMaps(sb.GetNamespace()).Delete(ctx, cm.Name, metav1.DeleteOptions{
TypeMeta: metav1.TypeMeta{},
Preconditions: &metav1.Preconditions{
UID: &cm.UID,
},
})
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to delete ConfigMap %s/%s: %w", cm.Namespace, cm.Name, err)
}

return nil
}
err := k8s.CoreV1().ConfigMaps(sb.GetNamespace()).Delete(ctx, cm.Name, metav1.DeleteOptions{
TypeMeta: metav1.TypeMeta{},
Preconditions: &metav1.Preconditions{
UID: &cm.UID,
ResourceVersion: &cm.ResourceVersion,
},
})
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to delete ConfigMap %s/%s: %w", cm.Namespace, cm.Name, err)
}

return nil
Expand Down
141 changes: 115 additions & 26 deletions pkg/reconciler/apiserversource/apiserversource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/system"

Expand Down Expand Up @@ -250,6 +251,7 @@ func TestReconcile(t *testing.T) {
rttestingv1.WithConfigMapLabels(metav1.LabelSelector{
MatchLabels: map[string]string{
eventingtls.TrustBundleLabelKey: eventingtls.TrustBundleLabelValue,
"x": "y",
},
}),
),
Expand All @@ -262,6 +264,87 @@ func TestReconcile(t *testing.T) {
},
WithReactors: []clientgotesting.ReactionFunc{subjectAccessReviewCreateReactor(true)},
SkipNamespaceValidation: true, // SubjectAccessReview objects are cluster-scoped.
}, {
Name: "trust bundle propagation - delete config map",
Objects: []runtime.Object{
rttestingv1.NewApiServerSource(sourceName, testNS,
rttestingv1.WithApiServerSourceSpec(sourcesv1.ApiServerSourceSpec{
Resources: []sourcesv1.APIVersionKindSelector{{
APIVersion: "v1",
Kind: "Namespace",
}},
SourceSpec: duckv1.SourceSpec{Sink: sinkDest},
}),
rttestingv1.WithApiServerSourceUID(sourceUID),
rttestingv1.WithApiServerSourceObjectMetaGeneration(generation),
),
rttestingv1.NewChannel(sinkName, testNS,
rttestingv1.WithInitChannelConditions,
rttestingv1.WithChannelAddress(sinkAddressable),
),
makeAvailableReceiveAdapter(t, withTrustBundle("bundle")),
rttestingv1.NewConfigMap("bundle"+eventingtls.TrustBundleConfigMapNameSuffix, testNS,
rttestingv1.WithConfigMapData(map[string]string{"a": "a"}),
func(cm *corev1.ConfigMap) {
cm.OwnerReferences = append(cm.OwnerReferences, metav1.OwnerReference{
APIVersion: "sources.knative.dev/v1",
Kind: "ApiServerSource",
Name: sourceName,
UID: sourceUID,
})
},
rttestingv1.WithConfigMapLabels(metav1.LabelSelector{
MatchLabels: map[string]string{
eventingtls.TrustBundleLabelKey: eventingtls.TrustBundleLabelValue,
},
}),
),
},
Key: testNS + "/" + sourceName,
WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
Object: rttestingv1.NewApiServerSource(sourceName, testNS,
rttestingv1.WithApiServerSourceSpec(sourcesv1.ApiServerSourceSpec{
Resources: []sourcesv1.APIVersionKindSelector{{
APIVersion: "v1",
Kind: "Namespace",
}},
SourceSpec: duckv1.SourceSpec{Sink: sinkDest},
}),
rttestingv1.WithApiServerSourceUID(sourceUID),
rttestingv1.WithApiServerSourceObjectMetaGeneration(generation),
// Status Update:
rttestingv1.WithInitApiServerSourceConditions,
rttestingv1.WithApiServerSourceDeployed,
rttestingv1.WithApiServerSourceSink(sinkURI),
rttestingv1.WithApiServerSourceSufficientPermissions,
rttestingv1.WithApiServerSourceReferenceModeEventTypes(source),
rttestingv1.WithApiServerSourceStatusObservedGeneration(generation),
rttestingv1.WithApiServerSourceStatusNamespaces([]string{testNS}),
rttestingv1.WithApiServerSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
}},
WantCreates: []runtime.Object{
makeSubjectAccessReview("namespaces", "get", "default"),
makeSubjectAccessReview("namespaces", "list", "default"),
makeSubjectAccessReview("namespaces", "watch", "default"),
},
WantDeletes: []clientgotesting.DeleteActionImpl{
{
ActionImpl: clientgotesting.ActionImpl{
Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"},
Namespace: system.Namespace(),
},
Name: "bundle" + eventingtls.TrustBundleConfigMapNameSuffix,
},
},
WantEvents: []string{
Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", sourceName),
},
WantPatches: []clientgotesting.PatchActionImpl{
patchFinalizers(sourceName, testNS),
},
WithReactors: []clientgotesting.ReactionFunc{subjectAccessReviewCreateReactor(true)},
SkipNamespaceValidation: true, // SubjectAccessReview objects are cluster-scoped.
}, {
Name: "trust bundle propagation - update RA",
Objects: []runtime.Object{
Expand Down Expand Up @@ -303,6 +386,7 @@ func TestReconcile(t *testing.T) {
rttestingv1.WithConfigMapLabels(metav1.LabelSelector{
MatchLabels: map[string]string{
eventingtls.TrustBundleLabelKey: eventingtls.TrustBundleLabelValue,
"x": "y",
},
}),
),
Expand Down Expand Up @@ -331,32 +415,7 @@ func TestReconcile(t *testing.T) {
),
}},
WantUpdates: []clientgotesting.UpdateActionImpl{{
Object: makeAvailableReceiveAdapter(t, func(deployment *appsv1.Deployment) {

volumeName := fmt.Sprintf("%s%s", eventingtls.TrustBundleVolumeNamePrefix, "volume")
deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
ConfigMap: &corev1.ConfigMapProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bundle" + eventingtls.TrustBundleConfigMapNameSuffix,
},
},
},
},
},
},
})

deployment.Spec.Template.Spec.Containers[0].VolumeMounts = append(deployment.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
Name: volumeName,
ReadOnly: true,
MountPath: eventingtls.TrustBundleMountPath,
})
}),
Object: makeAvailableReceiveAdapter(t, withTrustBundle("bundle")),
}},
WantCreates: []runtime.Object{
makeSubjectAccessReview("namespaces", "get", "default"),
Expand Down Expand Up @@ -447,6 +506,7 @@ func TestReconcile(t *testing.T) {
rttestingv1.WithConfigMapLabels(metav1.LabelSelector{
MatchLabels: map[string]string{
eventingtls.TrustBundleLabelKey: eventingtls.TrustBundleLabelValue,
"x": "y",
},
}),
),
Expand Down Expand Up @@ -1496,6 +1556,35 @@ func TestReconcile(t *testing.T) {
))
}

func withTrustBundle(name string) rttestingv1.DeploymentOption {
return func(deployment *appsv1.Deployment) {

volumeName := fmt.Sprintf("%s%s", eventingtls.TrustBundleVolumeNamePrefix, "volume")
deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
ConfigMap: &corev1.ConfigMapProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: name + eventingtls.TrustBundleConfigMapNameSuffix,
},
},
},
},
},
},
})

deployment.Spec.Template.Spec.Containers[0].VolumeMounts = append(deployment.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
Name: volumeName,
ReadOnly: true,
MountPath: eventingtls.TrustBundleMountPath,
})
}
}

func makeReceiveAdapter(t *testing.T, option ...rttestingv1.DeploymentOption) *appsv1.Deployment {
return makeReceiveAdapterWithName(t, sourceName, option...)
}
Expand Down

0 comments on commit 5355171

Please sign in to comment.