This is an automated email from the ASF dual-hosted git repository. pcongiusti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push: new 25f599324 Compute digest of configmap and secret from its data (#5115) 25f599324 is described below commit 25f59932476c12f81370fc8abcbacb6926f46fa9 Author: Lucie Krejcirova <lfabr...@redhat.com> AuthorDate: Mon Feb 12 18:43:25 2024 +0100 Compute digest of configmap and secret from its data (#5115) --- e2e/common/config/config_reload_test.go | 39 +++++++++++++++++ e2e/support/test_support.go | 25 +++++++++++ pkg/util/digest/digest.go | 61 +++++++++++++++++++++++--- pkg/util/digest/digest_test.go | 78 +++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 6 deletions(-) diff --git a/e2e/common/config/config_reload_test.go b/e2e/common/config/config_reload_test.go index cb43fac0f..94a18a86b 100644 --- a/e2e/common/config/config_reload_test.go +++ b/e2e/common/config/config_reload_test.go @@ -23,6 +23,7 @@ limitations under the License. package config import ( + "strconv" "testing" . "github.com/onsi/gomega" @@ -115,3 +116,41 @@ func TestSecretHotReload(t *testing.T) { Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) } + +func TestConfigmapWithOwnerRefHotReloadDefault(t *testing.T) { + CheckConfigmapWithOwnerRef(t, false) +} + +func TestConfigmapWithOwnerRefHotReload(t *testing.T) { + CheckConfigmapWithOwnerRef(t, true) +} + +func CheckConfigmapWithOwnerRef(t *testing.T, hotreload bool) { + RegisterTestingT(t) + name := RandomizedSuffixName("config-configmap-route") + cmName := RandomizedSuffixName("my-hot-cm-") + Expect(KamelRunWithID(operatorID, ns, "./files/config-configmap-route.groovy", + "--config", + "configmap:"+cmName, + "--name", + name, + "-t", + "mount.hot-reload="+strconv.FormatBool(hotreload), + ).Execute()).To(Succeed()) + + Eventually(IntegrationPhase(ns, name), TestTimeoutLong).Should(Equal(v1.IntegrationPhaseError)) + var cmData = make(map[string]string) + cmData["my-configmap-key"] = "my configmap content" + CreatePlainTextConfigmapWithOwnerRefWithLabels(ns, cmName, cmData, name, Integration(ns, name)().UID, map[string]string{"camel.apache.org/integration": "test"}) + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationLogs(ns, name), TestTimeoutLong).Should(ContainSubstring("my configmap content")) + cmData["my-configmap-key"] = "my configmap content updated" + UpdatePlainTextConfigmapWithLabels(ns, cmName, cmData, map[string]string{"camel.apache.org/integration": "test"}) + if hotreload { + Eventually(IntegrationLogs(ns, name), TestTimeoutLong).Should(ContainSubstring("my configmap content updated")) + } else { + Eventually(IntegrationLogs(ns, name), TestTimeoutLong).Should(Not(ContainSubstring("my configmap content updated"))) + } + Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) + DeleteConfigmap(ns, cmName) +} diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go index 457c3b0a9..4036206b5 100644 --- a/e2e/support/test_support.go +++ b/e2e/support/test_support.go @@ -1606,6 +1606,31 @@ func CreatePlainTextConfigmapWithLabels(ns string, name string, data map[string] return TestClient().Create(TestContext, &cm) } +func CreatePlainTextConfigmapWithOwnerRefWithLabels(ns string, name string, data map[string]string, orname string, uid types.UID, labels map[string]string) error { + cm := corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: corev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: "Integration", + Name: orname, + UID: uid, + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + Labels: labels, + }, + Data: data, + } + return TestClient().Create(TestContext, &cm) +} + func UpdatePlainTextConfigmap(ns string, name string, data map[string]string) error { return UpdatePlainTextConfigmapWithLabels(ns, name, data, nil) } diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go index 4965aedd9..52a9a01ae 100644 --- a/pkg/util/digest/digest.go +++ b/pkg/util/digest/digest.go @@ -24,7 +24,6 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" - "fmt" "hash" "io" "path/filepath" @@ -37,6 +36,8 @@ import ( "github.com/apache/camel-k/v2/pkg/util/defaults" "github.com/apache/camel-k/v2/pkg/util/dsl" corev1 "k8s.io/api/core/v1" + + "fmt" ) const ( @@ -136,15 +137,63 @@ func ComputeForIntegration(integration *v1.Integration, configmaps []*corev1.Con } } - // Configmap and secret content + // Configmap content for _, cm := range configmaps { - if _, err := hash.Write([]byte(cm.String())); err != nil { - return "", err + if cm != nil { + // name, ns + if _, err := hash.Write([]byte(fmt.Sprintf("%s/%s", cm.Name, cm.Namespace))); err != nil { + return "", err + } + // Data with sorted keys + if cm.Data != nil { + // sort keys + keys := make([]string, 0, len(cm.Data)) + for k := range cm.Data { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + if _, err := hash.Write([]byte(fmt.Sprintf("%s=%v,", k, cm.Data[k]))); err != nil { + return "", err + } + } + } + // BinaryData with sorted keys + if cm.BinaryData != nil { + keys := make([]string, 0, len(cm.BinaryData)) + for k := range cm.BinaryData { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + if _, err := hash.Write([]byte(fmt.Sprintf("%s=%v,", k, cm.BinaryData[k]))); err != nil { + return "", err + } + } + } } } + + // Secret content for _, s := range secrets { - if _, err := hash.Write([]byte(s.String())); err != nil { - return "", err + if s != nil { + // name, ns + if _, err := hash.Write([]byte(fmt.Sprintf("%s/%s", s.Name, s.Namespace))); err != nil { + return "", err + } + // Data with sorted keys + if s.Data != nil { + keys := make([]string, 0, len(s.Data)) + for k := range s.Data { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + if _, err := hash.Write([]byte(fmt.Sprintf("%s=%v,", k, s.Data[k]))); err != nil { + return "", err + } + } + } } } diff --git a/pkg/util/digest/digest_test.go b/pkg/util/digest/digest_test.go index 3139b88c3..a40a174a9 100644 --- a/pkg/util/digest/digest_test.go +++ b/pkg/util/digest/digest_test.go @@ -21,6 +21,12 @@ import ( "os" "testing" + "github.com/stretchr/testify/require" + + "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" "github.com/stretchr/testify/assert" ) @@ -60,3 +66,75 @@ func TestDigestSHA1FromTempFile(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "OXPdxTeLf5rqnsqvTi0CgmWoN/0=", sha1) } + +func TestDigestUsesConfigmap(t *testing.T) { + it := v1.Integration{ + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + Mount: &trait.MountTrait{ + Configs: []string{"configmap:cm"}, + HotReload: pointer.Bool(true), + }, + }, + }, + } + + digest1, err := ComputeForIntegration(&it, nil, nil) + require.NoError(t, err) + + cm := corev1.ConfigMap{ + Data: map[string]string{ + "foo": "bar", + }, + } + cms := []*corev1.ConfigMap{&cm} + + digest2, err := ComputeForIntegration(&it, cms, nil) + require.NoError(t, err) + assert.NotEqual(t, digest1, digest2) + + cm.Data["foo"] = "bar updated" + digest3, err := ComputeForIntegration(&it, cms, nil) + require.NoError(t, err) + assert.NotEqual(t, digest2, digest3) + + digest4, err := ComputeForIntegration(&it, cms, nil) + require.NoError(t, err) + assert.Equal(t, digest4, digest3) +} + +func TestDigestUsesSecret(t *testing.T) { + it := v1.Integration{ + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + Mount: &trait.MountTrait{ + Configs: []string{"secret:mysec"}, + HotReload: pointer.Bool(true), + }, + }, + }, + } + + digest1, err := ComputeForIntegration(&it, nil, nil) + require.NoError(t, err) + + sec := corev1.Secret{ + Data: map[string][]byte{ + "foo": []byte("bar"), + }, + StringData: map[string]string{ + "foo2": "bar2", + }, + } + + secrets := []*corev1.Secret{&sec} + + digest2, err := ComputeForIntegration(&it, nil, secrets) + require.NoError(t, err) + assert.NotEqual(t, digest1, digest2) + + sec.Data["foo"] = []byte("bar updated") + digest3, err := ComputeForIntegration(&it, nil, secrets) + require.NoError(t, err) + assert.NotEqual(t, digest2, digest3) +}