Skip to content

Commit 305247e

Browse files
committed
combine the various places where hashing happens into one single package
On-behalf-of: @SAP [email protected]
1 parent 5c1c7e3 commit 305247e

File tree

4 files changed

+62
-48
lines changed

4 files changed

+62
-48
lines changed

internal/controller/apiresourceschema/controller.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ package apiresourceschema
1818

1919
import (
2020
"context"
21-
"crypto/sha1"
22-
"encoding/hex"
23-
"encoding/json"
2421
"fmt"
2522
"reflect"
2623
"strings"
@@ -29,6 +26,7 @@ import (
2926
"go.uber.org/zap"
3027

3128
"github.com/kcp-dev/api-syncagent/internal/controllerutil/predicate"
29+
"github.com/kcp-dev/api-syncagent/internal/crypto"
3230
"github.com/kcp-dev/api-syncagent/internal/discovery"
3331
"github.com/kcp-dev/api-syncagent/internal/projection"
3432
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
@@ -255,15 +253,7 @@ func (r *Reconciler) applyProjection(apiGroup string, crd *apiextensionsv1.Custo
255253
// getAPIResourceSchemaName generates the name for the ARS in kcp. Note that
256254
// kcp requires, just like CRDs, that ARS are named following a specific pattern.
257255
func (r *Reconciler) getAPIResourceSchemaName(apiGroup string, crd *apiextensionsv1.CustomResourceDefinition) string {
258-
hash := sha1.New()
259-
if err := json.NewEncoder(hash).Encode(crd.Spec.Names); err != nil {
260-
// This is not something that should ever happen at runtime and is also not
261-
// something we can really gracefully handle, so crashing and restarting might
262-
// be a good way to signal the service owner that something is up.
263-
panic(fmt.Sprintf("Failed to hash PublishedResource source: %v", err))
264-
}
265-
266-
checksum := hex.EncodeToString(hash.Sum(nil))
256+
checksum := crypto.Hash(crd.Spec.Names)
267257

268258
// include a leading "v" to prevent SHA-1 hashes with digits to break the name
269259
return fmt.Sprintf("v%s.%s.%s", checksum[:8], crd.Spec.Names.Plural, apiGroup)

internal/crypto/hash.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2025 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package crypto
18+
19+
import (
20+
"crypto/sha1"
21+
"encoding/hex"
22+
"encoding/json"
23+
"fmt"
24+
)
25+
26+
func Hash(data any) string {
27+
hash := sha1.New()
28+
29+
var err error
30+
switch asserted := data.(type) {
31+
case string:
32+
_, err = hash.Write([]byte(asserted))
33+
case []byte:
34+
_, err = hash.Write(asserted)
35+
default:
36+
err = json.NewEncoder(hash).Encode(data)
37+
}
38+
39+
if err != nil {
40+
// This is not something that should ever happen at runtime and is also not
41+
// something we can really gracefully handle, so crashing and restarting might
42+
// be a good way to signal the service owner that something is up.
43+
panic(fmt.Sprintf("Failed to hash: %v", err))
44+
}
45+
46+
return hex.EncodeToString(hash.Sum(nil))
47+
}
48+
49+
func ShortHash(data any) string {
50+
return Hash(data)[:20]
51+
}

internal/projection/naming.go

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ limitations under the License.
1717
package projection
1818

1919
import (
20-
"crypto/sha1"
21-
"encoding/hex"
2220
"fmt"
2321
"strings"
2422

2523
"github.com/kcp-dev/logicalcluster/v3"
2624

25+
"github.com/kcp-dev/api-syncagent/internal/crypto"
2726
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
2827

2928
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -44,9 +43,9 @@ func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object met
4443
replacer := strings.NewReplacer(
4544
// order of elements is important here, "$fooHash" needs to be defined before "$foo"
4645
syncagentv1alpha1.PlaceholderRemoteClusterName, clusterName.String(),
47-
syncagentv1alpha1.PlaceholderRemoteNamespaceHash, shortSha1Hash(object.GetNamespace()),
46+
syncagentv1alpha1.PlaceholderRemoteNamespaceHash, crypto.ShortHash(object.GetNamespace()),
4847
syncagentv1alpha1.PlaceholderRemoteNamespace, object.GetNamespace(),
49-
syncagentv1alpha1.PlaceholderRemoteNameHash, shortSha1Hash(object.GetName()),
48+
syncagentv1alpha1.PlaceholderRemoteNameHash, crypto.ShortHash(object.GetName()),
5049
syncagentv1alpha1.PlaceholderRemoteName, object.GetName(),
5150
)
5251

@@ -68,17 +67,3 @@ func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object met
6867

6968
return result
7069
}
71-
72-
func shortSha1Hash(value string) string {
73-
hash := sha1.New()
74-
if _, err := hash.Write([]byte(value)); err != nil {
75-
// This is not something that should ever happen at runtime and is also not
76-
// something we can really gracefully handle, so crashing and restarting might
77-
// be a good way to signal the service owner that something is up.
78-
panic(fmt.Sprintf("Failed to hash string: %v", err))
79-
}
80-
81-
encoded := hex.EncodeToString(hash.Sum(nil))
82-
83-
return encoded[:20]
84-
}

internal/sync/state_store.go

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ limitations under the License.
1717
package sync
1818

1919
import (
20-
"crypto/sha1"
21-
"encoding/hex"
22-
"encoding/json"
2320
"fmt"
2421
"strings"
2522

23+
"github.com/kcp-dev/api-syncagent/internal/crypto"
2624
"github.com/kcp-dev/logicalcluster/v3"
2725

2826
corev1 "k8s.io/api/core/v1"
@@ -112,34 +110,24 @@ type kubernetesBackend struct {
112110
}
113111

114112
func hashObject(obj *unstructured.Unstructured) string {
115-
data := map[string]any{
113+
return crypto.ShortHash(map[string]any{
116114
"apiVersion": obj.GetAPIVersion(),
115+
"kind": obj.GetKind(),
117116
"namespace": obj.GetNamespace(),
118117
"name": obj.GetName(),
119-
}
120-
121-
hash := sha1.New()
122-
123-
if err := json.NewEncoder(hash).Encode(data); err != nil {
124-
// This is not something that should ever happen at runtime and is also not
125-
// something we can really gracefully handle, so crashing and restarting might
126-
// be a good way to signal the service owner that something is up.
127-
panic(fmt.Sprintf("Failed to hash object key: %v", err))
128-
}
129-
130-
return hex.EncodeToString(hash.Sum(nil))
118+
})
131119
}
132120

133121
func newKubernetesBackend(namespace string, primaryObject, stateCluster syncSide) *kubernetesBackend {
134-
keyHash := hashObject(primaryObject.object)
122+
shortKeyHash := hashObject(primaryObject.object)
135123

136124
secretLabels := newObjectKey(primaryObject.object, primaryObject.clusterName, primaryObject.workspacePath).Labels()
137125
secretLabels[objectStateLabelName] = objectStateLabelValue
138126

139127
return &kubernetesBackend{
140128
secretName: types.NamespacedName{
141129
// trim hash down; 20 was chosen at random
142-
Name: fmt.Sprintf("obj-state-%s-%s", primaryObject.clusterName, keyHash[:20]),
130+
Name: fmt.Sprintf("obj-state-%s-%s", primaryObject.clusterName, shortKeyHash),
143131
Namespace: namespace,
144132
},
145133
labels: secretLabels,

0 commit comments

Comments
 (0)