mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
refactor(hash): review nits — shared helpers, hardened reflection walker
The trimming of hash-excluded fields becomes trimServiceHashFields, shared between ServiceHash and the continuity test so the exclusion set cannot silently drift; pinRootKeyOrder reuses the package's sortedKeys helper; the key-order coverage walker now follows encoding/json promotion rules for non-struct embeds and unexported fields. Golden hashes unchanged. Signed-off-by: Nicolas De Loof <[email protected]>
This commit is contained in:
committed by
Guillaume Lours
parent
725fe535c0
commit
0c68d08f26
+12
-10
@@ -19,7 +19,6 @@ package compose
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
@@ -144,9 +143,11 @@ var serviceHashKeyOrder = []string{
|
||||
"pre_stop",
|
||||
}
|
||||
|
||||
// ServiceHash computes the configuration hash for a service.
|
||||
func ServiceHash(o types.ServiceConfig) (string, error) {
|
||||
// remove the Build config when generating the service hash
|
||||
// trimServiceHashFields removes the attributes deliberately excluded from the
|
||||
// service config-hash (build inputs, scaling knobs, dependency wiring). It is
|
||||
// the single definition of that exclusion set, shared with the continuity
|
||||
// test so the two cannot silently drift apart.
|
||||
func trimServiceHashFields(o types.ServiceConfig) types.ServiceConfig {
|
||||
o.Build = nil
|
||||
o.PullPolicy = ""
|
||||
o.Scale = nil
|
||||
@@ -157,6 +158,12 @@ func ServiceHash(o types.ServiceConfig) (string, error) {
|
||||
}
|
||||
o.DependsOn = nil
|
||||
o.Profiles = nil
|
||||
return o
|
||||
}
|
||||
|
||||
// ServiceHash computes the configuration hash for a service.
|
||||
func ServiceHash(o types.ServiceConfig) (string, error) {
|
||||
o = trimServiceHashFields(o)
|
||||
|
||||
raw, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
@@ -197,12 +204,7 @@ func pinRootKeyOrder(raw []byte, order []string) ([]byte, error) {
|
||||
delete(root, key)
|
||||
}
|
||||
}
|
||||
rest := make([]string, 0, len(root))
|
||||
for key := range root {
|
||||
rest = append(rest, key)
|
||||
}
|
||||
sort.Strings(rest)
|
||||
for _, key := range rest {
|
||||
for _, key := range sortedKeys(root) {
|
||||
write(key, root[key])
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
|
||||
+12
-14
@@ -57,18 +57,7 @@ func TestServiceHashContinuity(t *testing.T) {
|
||||
pinned, err := ServiceHash(svc)
|
||||
assert.NilError(t, err)
|
||||
|
||||
o := svc
|
||||
o.Build = nil
|
||||
o.PullPolicy = ""
|
||||
o.Scale = nil
|
||||
if o.Deploy != nil {
|
||||
deploy := *o.Deploy
|
||||
deploy.Replicas = nil
|
||||
o.Deploy = &deploy
|
||||
}
|
||||
o.DependsOn = nil
|
||||
o.Profiles = nil
|
||||
raw, err := json.Marshal(o)
|
||||
raw, err := json.Marshal(trimServiceHashFields(svc))
|
||||
assert.NilError(t, err)
|
||||
legacy := digest.SHA256.FromBytes(raw).Encoded()
|
||||
assert.Equal(t, pinned, legacy)
|
||||
@@ -180,12 +169,21 @@ func TestServiceHashKeyOrderCoversStruct(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
if f.Anonymous && name == "" {
|
||||
// encoding/json dereferences embedded pointers; match it
|
||||
// encoding/json dereferences embedded pointers and promotes
|
||||
// the exported fields of even an unexported struct embed;
|
||||
// match it, and leave non-struct embeds (named types,
|
||||
// interfaces) to the regular field handling below
|
||||
ft := f.Type
|
||||
if ft.Kind() == reflect.Pointer {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
walk(ft)
|
||||
if ft.Kind() == reflect.Struct {
|
||||
walk(ft)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !f.IsExported() {
|
||||
// unexported fields never reach the JSON output
|
||||
continue
|
||||
}
|
||||
if name == "" {
|
||||
|
||||
Reference in New Issue
Block a user