This is an automated email from the ASF dual-hosted git repository.

tsato pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 4e909c8b075d8318a471893011e76136427b24bd
Author: Tadayoshi Sato <[email protected]>
AuthorDate: Sun Jul 3 23:04:28 2022 +0900

    fix(digest): fix hash computation for the new Traits schema
---
 pkg/util/digest/digest.go | 82 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 79 insertions(+), 3 deletions(-)

diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index cdeee2834..1ddda2059 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -25,6 +25,7 @@ import (
        "encoding/base64"
        "encoding/json"
        "fmt"
+       "hash"
        "io"
        "path"
        "sort"
@@ -98,12 +99,29 @@ func ComputeForIntegration(integration *v1.Integration) 
(string, error) {
        }
 
        // Integration traits
-       traits, err := json.Marshal(integration.Spec.Traits)
+       // Calculation logic prior to 1.10.0 (the new Traits API schema) is 
maintained
+       // in order to keep consistency in the digest calculated from the same 
set of
+       // Trait configurations for backward compatibility.
+       traitsMap, err := toMap(integration.Spec.Traits)
        if err != nil {
                return "", err
        }
-       if _, err := hash.Write(traits); err != nil {
-               return "", err
+       for _, name := range sortedTraitsMapKeys(traitsMap) {
+               if name != "addons" {
+                       if err := computeForTrait(hash, name, traitsMap[name]); 
err != nil {
+                               return "", err
+                       }
+               } else {
+                       // Addons
+                       addons := traitsMap["addons"]
+                       for _, name := range util.SortedMapKeys(addons) {
+                               if addon, ok := 
addons[name].(map[string]interface{}); ok {
+                                       if err := computeForTrait(hash, name, 
addon); err != nil {
+                                               return "", err
+                                       }
+                               }
+                       }
+               }
        }
        // Integration traits as annotations
        for _, k := range sortedTraitAnnotationsKeys(integration) {
@@ -118,6 +136,53 @@ func ComputeForIntegration(integration *v1.Integration) 
(string, error) {
        return digest, nil
 }
 
+func computeForTrait(hash hash.Hash, name string, trait 
map[string]interface{}) error {
+       if _, err := hash.Write([]byte(name + "[")); err != nil {
+               return err
+       }
+       // hash legacy configuration first
+       if trait["configuration"] != nil {
+               if config, ok := 
trait["configuration"].(map[string]interface{}); ok {
+                       if err := computeForTraitProps(hash, config); err != 
nil {
+                               return err
+                       }
+               }
+               delete(trait, "configuration")
+       }
+       if err := computeForTraitProps(hash, trait); err != nil {
+               return err
+       }
+       if _, err := hash.Write([]byte("]")); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func computeForTraitProps(hash hash.Hash, props map[string]interface{}) error {
+       for _, prop := range util.SortedMapKeys(props) {
+               val := props[prop]
+               if _, err := hash.Write([]byte(fmt.Sprintf("%s=%v,", prop, 
val))); err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func toMap(traits v1.Traits) (map[string]map[string]interface{}, error) {
+       data, err := json.Marshal(traits)
+       if err != nil {
+               return nil, err
+       }
+       traitsMap := make(map[string]map[string]interface{})
+       if err = json.Unmarshal(data, &traitsMap); err != nil {
+               return nil, err
+       }
+
+       return traitsMap, nil
+}
+
 // ComputeForIntegrationKit a digest of the fields that are relevant for the 
deployment
 // Produces a digest that can be used as docker image tag.
 func ComputeForIntegrationKit(kit *v1.IntegrationKit) (string, error) {
@@ -216,6 +281,17 @@ func ComputeForSource(s v1.SourceSpec) (string, error) {
        return digest, nil
 }
 
+func sortedTraitsMapKeys(m map[string]map[string]interface{}) []string {
+       res := make([]string, len(m))
+       i := 0
+       for k := range m {
+               res[i] = k
+               i++
+       }
+       sort.Strings(res)
+       return res
+}
+
 func sortedTraitAnnotationsKeys(it *v1.Integration) []string {
        res := make([]string, 0, len(it.Annotations))
        for k := range it.Annotations {

Reply via email to