Skip to content

Commit

Permalink
When Generators change, reconcile the EventTrigger
Browse files Browse the repository at this point in the history
ConfigMapGenerator and SecretGenerator namespaces/names only depend
on the matching cluster. So those can be fetched to evaluate hash
of EventTrigger.

This PR adds code to requeue EventTrigger reconciliation when those
changes.
  • Loading branch information
mgianluc committed Nov 21, 2024
1 parent 2846400 commit ff12e2f
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 52 deletions.
2 changes: 1 addition & 1 deletion api/v1beta1/eventtrigger_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const (

type GeneratorReference struct {
// Namespace of the referenced resource.
// Nnamespace can be left empty. In such a case, namespace will
// Namespace can be left empty. In such a case, namespace will
// be implicit set to cluster's namespace.
// +optional
Namespace string `json:"namespace,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions config/crd/bases/lib.projectsveltos.io_eventtriggers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ spec:
namespace:
description: |-
Namespace of the referenced resource.
Nnamespace can be left empty. In such a case, namespace will
Namespace can be left empty. In such a case, namespace will
be implicit set to cluster's namespace.
type: string
required:
Expand Down Expand Up @@ -1783,7 +1783,7 @@ spec:
namespace:
description: |-
Namespace of the referenced resource.
Nnamespace can be left empty. In such a case, namespace will
Namespace can be left empty. In such a case, namespace will
be implicit set to cluster's namespace.
type: string
required:
Expand Down
53 changes: 15 additions & 38 deletions controllers/eventtrigger_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,35 +310,6 @@ func eventTriggerHash(ctx context.Context, c client.Client,
}
}

clusterType := clusterproxy.GetClusterType(cluster)
clusterObj, err := fecthClusterObjects(ctx, c, cluster.Namespace, cluster.Name, clusterType, logger)
if err == nil {
objects := currentObjects{
Cluster: clusterObj,
}

templateName := getTemplateName(cluster.Namespace, cluster.Name, e.Name)
local, remote, _ := fetchPolicyRefs(ctx, c, e, cluster, objects, templateName, logger)
for i := range local {
config += render.AsCode(local[i])
}
for i := range remote {
config += render.AsCode(remote[i])
}

for i := range e.Spec.HelmCharts {
valuesFromHash := getValuesFromHash(ctx, c, e.Spec.HelmCharts[i].ValuesFrom, cluster.Namespace,
templateName, objects, logger)
config += render.AsCode(valuesFromHash)
}

for i := range e.Spec.KustomizationRefs {
valuesFromHash := getValuesFromHash(ctx, c, e.Spec.KustomizationRefs[i].ValuesFrom, cluster.Namespace,
templateName, objects, logger)
config += render.AsCode(valuesFromHash)
}
}

h := sha256.New()
h.Write([]byte(config))
return h.Sum(nil), nil
Expand Down Expand Up @@ -2336,11 +2307,20 @@ func instantiateResourceFromGenerator(ctx context.Context, c client.Client, gene
if apierrors.IsNotFound(err) {
logger.V(logs.LogInfo).Info(fmt.Sprintf("%s %s/%s does not exist yet",
kind, namespace, referencedName))
return nil, nil
return nil, fmt.Errorf("referenced resource %s %s/%s does not exist yet",
kind, namespace, referencedName)
}
return nil, err
}

// Keep track of all referenced Generators. When those changes, EventTrigger will be reconciled.
resourceTracker := getTrackerInstance()
resourceTracker.trackResourceForConsumer(
&corev1.ObjectReference{Kind: referencedResource.GetObjectKind().GroupVersionKind().Kind,
Namespace: referencedResource.GetNamespace(), Name: referencedResource.GetName(), APIVersion: "v1"},
&corev1.ObjectReference{Kind: v1beta1.EventTriggerKind, Name: e.GetName(), APIVersion: v1beta1.GroupVersion.String()},
)

instantiatedName, err := instantiateSection(templateName, []byte(generator.InstantiatedResourceNameFormat), data, logger)
if err != nil {
logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to instantiate %q: %v", generator.InstantiatedResourceNameFormat, err))
Expand All @@ -2355,10 +2335,10 @@ func instantiateResourceFromGenerator(ctx context.Context, c client.Client, gene
return info, nil
}

func getValuesFromHash(ctx context.Context, c client.Client, valuesFrom []configv1beta1.ValueFrom,
clusterNamespace, templateName string, data any, logger logr.Logger) []byte {
func getValuesFrom(ctx context.Context, c client.Client, valuesFrom []configv1beta1.ValueFrom,
clusterNamespace, templateName string, data any, logger logr.Logger) []client.Object {

hash := ""
result := make([]client.Object, 0, len(valuesFrom))
for i := range valuesFrom {
ref := &valuesFrom[i]

Expand All @@ -2379,10 +2359,7 @@ func getValuesFromHash(ctx context.Context, c client.Client, valuesFrom []config
continue
}

hash += render.AsCode(resource)
result = append(result, resource)
}

h := sha256.New()
h.Write([]byte(hash))
return h.Sum(nil)
return result
}
6 changes: 4 additions & 2 deletions controllers/eventtrigger_deployer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,9 @@ var _ = Describe("EventTrigger deployer", func() {

initObjects := []client.Object{
secret,
configMap,
cluster,
e,
configMap,
eventSource,
eventReport,
}
Expand All @@ -353,7 +354,8 @@ var _ = Describe("EventTrigger deployer", func() {
config += render.AsCode(e.Labels)
config += render.AsCode(eventSource.Spec)
config += render.AsCode(eventReport.Spec)
// Content of referenced resources in PolicyRef/ValuesFrom is not included
config += render.AsCode(configMap.Data)
config += render.AsCode(secret.Data)
h := sha256.New()
h.Write([]byte(config))
expectedHash := h.Sum(nil)
Expand Down
102 changes: 96 additions & 6 deletions controllers/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import (
// fetchReferencedResources fetches resources referenced by EventTrigger.
// This includes:
// - EventSource and corresponding EventReports (from the passed in cluster only);
// - ConfigMaps referenced in the ConfigMapGenerator section
// - Secrets referenced in the SecretGenerator section
func fetchReferencedResources(ctx context.Context, c client.Client,
e *v1beta1.EventTrigger, cluster *corev1.ObjectReference, logger logr.Logger) ([]client.Object, error) {

Expand Down Expand Up @@ -66,20 +68,108 @@ func fetchReferencedResources(ctx context.Context, c client.Client,
if err != nil {
return nil, err
}

for i := range eventReports.Items {
result = append(result, &eventReports.Items[i])
}

// Resources references in PolicyRefs and/or ValuesFrom are not
// considered. Those resource namespace/name info can be expressed
// as template and so for different clusters/events different resources
// might be used.
// Also, EventTrigger should deploy ClusterProfile based on the state
// in the cluster when the event happened.
clusterType := clusterproxy.GetClusterType(cluster)
clusterObj, err := fecthClusterObjects(ctx, c, cluster.Namespace, cluster.Name, clusterType, logger)
if err == nil {
objects := currentObjects{
Cluster: clusterObj,
}

templateName := getTemplateName(cluster.Namespace, cluster.Name, e.Name)

referencedResources, err := collectResourcesFromConfigMapGenerators(ctx, c, objects, e,
cluster.Namespace, templateName, logger)
if err != nil {
return nil, err
}
result = append(result, referencedResources...)

referencedResources, err = collectResourcesFromSecretGenerators(ctx, c, objects, e,
cluster.Namespace, templateName, logger)
if err != nil {
return nil, err
}
result = append(result, referencedResources...)

local, remote, _ := fetchPolicyRefs(ctx, c, e, cluster, objects, templateName, logger)
result = append(result, local...)
result = append(result, remote...)

for i := range e.Spec.HelmCharts {
valuesFrom := getValuesFrom(ctx, c, e.Spec.HelmCharts[i].ValuesFrom, cluster.Namespace,
templateName, objects, logger)
result = append(result, valuesFrom...)
}

for i := range e.Spec.KustomizationRefs {
valuesFrom := getValuesFrom(ctx, c, e.Spec.KustomizationRefs[i].ValuesFrom, cluster.Namespace,
templateName, objects, logger)
result = append(result, valuesFrom...)
}
}

return result, nil
}

func collectResourcesFromConfigMapGenerators(ctx context.Context, c client.Client, objects any,
e *v1beta1.EventTrigger, templateName, clusterNamespace string, logger logr.Logger) ([]client.Object, error) {

results := make([]client.Object, len(e.Spec.ConfigMapGenerator))

for i := range e.Spec.ConfigMapGenerator {
generator := &e.Spec.ConfigMapGenerator[i]
namespace := libsveltostemplate.GetReferenceResourceNamespace(clusterNamespace, generator.Namespace)

// The name of the referenced resource can be expressed as a template
referencedName, err := instantiateSection(templateName, []byte(generator.Name), objects, logger)
if err != nil {
return nil, err
}

var referencedResource client.Object
referencedResource, err = getConfigMap(ctx, c, types.NamespacedName{Namespace: namespace, Name: string(referencedName)})
if err != nil {
return nil, err
}

results[i] = referencedResource
}

return results, nil
}

func collectResourcesFromSecretGenerators(ctx context.Context, c client.Client, objects any,
e *v1beta1.EventTrigger, templateName, clusterNamespace string, logger logr.Logger) ([]client.Object, error) {

results := make([]client.Object, len(e.Spec.SecretGenerator))

for i := range e.Spec.SecretGenerator {
generator := &e.Spec.SecretGenerator[i]
namespace := libsveltostemplate.GetReferenceResourceNamespace(clusterNamespace, generator.Namespace)

// The name of the referenced resource can be expressed as a template
referencedName, err := instantiateSection(templateName, []byte(generator.Name), objects, logger)
if err != nil {
return nil, err
}

var referencedResource client.Object
referencedResource, err = getSecret(ctx, c, types.NamespacedName{Namespace: namespace, Name: string(referencedName)})
if err != nil {
return nil, err
}

results[i] = referencedResource
}

return results, nil
}

// fetchEventSource fetches referenced EventSource
func fetchEventSource(ctx context.Context, c client.Client,
clusterNamespace, clusterName, eventSourceName string, clusterType libsveltosv1beta1.ClusterType,
Expand Down
3 changes: 2 additions & 1 deletion controllers/fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ var _ = Describe("Fetcher", func() {

initObjects := []client.Object{
secret,
cluster,
configMap,
e,
eventSource,
Expand All @@ -370,6 +371,6 @@ var _ = Describe("Fetcher", func() {

result, err := controllers.FetchReferencedResources(context.TODO(), c, e, getClusterRef(cluster), logger)
Expect(err).To(BeNil())
Expect(len(result)).To(Equal(2)) // EventSource + EventReport (no referenced resources)
Expect(len(result)).To(Equal(4)) // EventSource + EventReport + Referenced Resources
})
})
4 changes: 2 additions & 2 deletions manifest/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,7 @@ spec:
namespace:
description: |-
Namespace of the referenced resource.
Nnamespace can be left empty. In such a case, namespace will
Namespace can be left empty. In such a case, namespace will
be implicit set to cluster's namespace.
type: string
required:
Expand Down Expand Up @@ -1798,7 +1798,7 @@ spec:
namespace:
description: |-
Namespace of the referenced resource.
Nnamespace can be left empty. In such a case, namespace will
Namespace can be left empty. In such a case, namespace will
be implicit set to cluster's namespace.
type: string
required:
Expand Down
Loading

0 comments on commit ff12e2f

Please sign in to comment.