Skip to content

Commit fc55a94

Browse files
committed
refactor: move the config-hash helpers to internal/confighash
ServiceHash, NetworkHash and VolumeHash only serve Compose's own change detection (the config-hash labels stamped on containers, networks and volumes); they have no business being part of the exported API of pkg/compose. Move them, with their tests, to internal/confighash. The hashes are byte-for-byte unchanged (the golden values test moves with the code, and `compose config --hash` prints the same output before and after). The only dependency on the package they came from, a sorted-keys helper, is replaced by slices.Sorted(maps.Keys(...)). This removes exported symbols from the /v5 module: compose.ServiceHash, compose.NetworkHash and compose.VolumeHash. Part of #14396 Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
1 parent 343bb38 commit fc55a94

8 files changed

Lines changed: 34 additions & 19 deletions

File tree

‎cmd/compose/config.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"go.yaml.in/yaml/v4"
3737

3838
"github.com/docker/compose/v5/cmd/formatter"
39+
"github.com/docker/compose/v5/internal/confighash"
3940
"github.com/docker/compose/v5/pkg/api"
4041
"github.com/docker/compose/v5/pkg/compose"
4142
)
@@ -672,7 +673,7 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
672673
return err
673674
}
674675

675-
hash, err := compose.ServiceHash(s)
676+
hash, err := confighash.ServiceHash(s)
676677
if err != nil {
677678
return err
678679
}
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414
limitations under the License.
1515
*/
1616

17-
package compose
17+
// Package confighash computes the configuration hashes that Compose stamps on
18+
// the containers, networks and volumes it creates, and compares against on
19+
// the next run to detect a configuration change.
20+
//
21+
// The hashes are persisted in labels of existing resources: the byte layout
22+
// they are computed from is frozen, any change recreates users' resources.
23+
package confighash
1824

1925
import (
2026
"bytes"
2127
"encoding/json"
28+
"maps"
29+
"slices"
2230

2331
"github.com/compose-spec/compose-go/v2/types"
2432
"github.com/opencontainers/go-digest"
@@ -212,7 +220,7 @@ func pinRootKeyOrder(raw []byte, order []string) ([]byte, error) {
212220
delete(root, key)
213221
}
214222
}
215-
for _, key := range sortedKeys(root) {
223+
for _, key := range slices.Sorted(maps.Keys(root)) {
216224
write(key, root[key])
217225
}
218226
buf.WriteByte('}')
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
limitations under the License.
1515
*/
1616

17-
package compose
17+
package confighash
1818

1919
import (
2020
"encoding/json"

‎pkg/compose/create.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/sirupsen/logrus"
4343
cdi "tags.cncf.io/container-device-interface/pkg/parser"
4444

45+
"github.com/docker/compose/v5/internal/confighash"
4546
"github.com/docker/compose/v5/pkg/api"
4647
)
4748

@@ -305,7 +306,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
305306
) (createConfigs, error) {
306307
labels := options.Labels
307308
if !options.NoConfigHash {
308-
hash, err := ServiceHash(service)
309+
hash, err := confighash.ServiceHash(service)
309310
if err != nil {
310311
return createConfigs{}, err
311312
}
@@ -1501,7 +1502,7 @@ func (s *composeService) createNetwork(ctx context.Context, n *types.NetworkConf
15011502
Options: n.Ipam.Options,
15021503
}
15031504
}
1504-
hash, err := NetworkHash(n)
1505+
hash, err := confighash.NetworkHash(n)
15051506
if err != nil {
15061507
return err
15071508
}
@@ -1590,7 +1591,7 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
15901591
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
15911592
eventName := "Volume " + volume.Name
15921593
s.events.On(creatingEvent(eventName))
1593-
hash, err := VolumeHash(volume)
1594+
hash, err := confighash.VolumeHash(volume)
15941595
if err != nil {
15951596
return err
15961597
}

‎pkg/compose/dependency_wait_test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"go.uber.org/mock/gomock"
3333
"gotest.tools/v3/assert"
3434

35+
"github.com/docker/compose/v5/internal/confighash"
3536
"github.com/docker/compose/v5/pkg/api"
3637
"github.com/docker/compose/v5/pkg/mocks"
3738
)
@@ -76,7 +77,7 @@ func TestWaitDependencies(t *testing.T) {
7677
},
7778
}}
7879

79-
_, err := ServiceHash(project.Services["disabled"])
80+
_, err := confighash.ServiceHash(project.Services["disabled"])
8081
assert.NilError(t, err)
8182

8283
assert.NilError(t, tested.(*composeService).waitDependencies(

‎pkg/compose/reconcile.go‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
mmount "github.com/moby/moby/api/types/mount"
3131
"github.com/sirupsen/logrus"
3232

33+
"github.com/docker/compose/v5/internal/confighash"
3334
"github.com/docker/compose/v5/pkg/api"
3435
)
3536

@@ -255,7 +256,7 @@ func (r *reconciler) resolveObserved() {
255256
// recreation. Unlike volumes, recreating a network is not destructive, so no
256257
// user confirmation is required.
257258
//
258-
// Divergence is detected by comparing NetworkHash(desired) with the config-hash
259+
// Divergence is detected by comparing confighash.NetworkHash(desired) with the config-hash
259260
// persisted on the live network (observed.ConfigHash). A network with no
260261
// recorded hash (e.g. created by an older Compose or manually) is left
261262
// untouched: without a recorded hash there is no reliable way to tell
@@ -278,7 +279,7 @@ func (r *reconciler) reconcileNetworks() error {
278279
r.planCreateNetwork(key, &desired, "not found")
279280
continue
280281
}
281-
expected, err := NetworkHash(&desired)
282+
expected, err := confighash.NetworkHash(&desired)
282283
if err != nil {
283284
return err
284285
}
@@ -421,7 +422,7 @@ func (r *reconciler) planRecreateNetworks(keys []string) {
421422
// for volumes whose configuration has diverged from the live resource,
422423
// recreation — gated on user confirmation because it destroys the volume's data.
423424
//
424-
// Divergence is detected by comparing VolumeHash(desired) with the config-hash
425+
// Divergence is detected by comparing confighash.VolumeHash(desired) with the config-hash
425426
// persisted on the live volume (observed.ConfigHash). A volume with no recorded
426427
// hash (e.g. created by an older Compose) is left untouched: without a
427428
// recorded hash there is no reliable way to tell configuration drift apart
@@ -438,7 +439,7 @@ func (r *reconciler) reconcileVolumes() error {
438439
r.planCreateVolume(key, &desired, "not found")
439440
continue
440441
}
441-
expected, err := VolumeHash(desired)
442+
expected, err := confighash.VolumeHash(desired)
442443
if err != nil {
443444
return err
444445
}
@@ -1083,7 +1084,7 @@ func serviceHashWithResolvedRefs(service types.ServiceConfig, containers map[str
10831084
resolved := service
10841085
resolved.VolumesFrom = slices.Clone(service.VolumesFrom)
10851086
_ = resolveServiceReferences(&resolved, containers)
1086-
return ServiceHash(resolved)
1087+
return confighash.ServiceHash(resolved)
10871088
}
10881089

10891090
// hasNetworkMismatch checks if the container is not connected to all expected networks.

‎pkg/compose/reconcile_test.go‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
logrustest "github.com/sirupsen/logrus/hooks/test"
3030
"gotest.tools/v3/assert"
3131

32+
"github.com/docker/compose/v5/internal/confighash"
3233
"github.com/docker/compose/v5/pkg/api"
3334
)
3435

@@ -95,7 +96,7 @@ func TestReconcileNetworks_CreateMissing(t *testing.T) {
9596

9697
func TestReconcileNetworks_ExistingMatch(t *testing.T) {
9798
nw := types.NetworkConfig{Name: "myproject_frontend"}
98-
hash, err := NetworkHash(&nw)
99+
hash, err := confighash.NetworkHash(&nw)
99100
assert.NilError(t, err)
100101

101102
project := &types.Project{
@@ -447,7 +448,7 @@ func TestReconcileVolumes_CreateMissing(t *testing.T) {
447448

448449
func TestReconcileVolumes_ExistingMatch(t *testing.T) {
449450
vol := types.VolumeConfig{Name: "myproject_data", Driver: "local"}
450-
hash, err := VolumeHash(vol)
451+
hash, err := confighash.VolumeHash(vol)
451452
assert.NilError(t, err)
452453

453454
project := &types.Project{
@@ -2002,21 +2003,21 @@ func TestReconcileContainers_RegularDependsOn_NoCascade(t *testing.T) {
20022003

20032004
func mustServiceHash(t *testing.T, svc types.ServiceConfig) string {
20042005
t.Helper()
2005-
h, err := ServiceHash(svc)
2006+
h, err := confighash.ServiceHash(svc)
20062007
assert.NilError(t, err)
20072008
return h
20082009
}
20092010

20102011
func mustVolumeHash(t *testing.T, vol types.VolumeConfig) string {
20112012
t.Helper()
2012-
h, err := VolumeHash(vol)
2013+
h, err := confighash.VolumeHash(vol)
20132014
assert.NilError(t, err)
20142015
return h
20152016
}
20162017

20172018
func mustNetworkHash(t *testing.T, nw types.NetworkConfig) string {
20182019
t.Helper()
2019-
h, err := NetworkHash(&nw)
2020+
h, err := confighash.NetworkHash(&nw)
20202021
assert.NilError(t, err)
20212022
return h
20222023
}

‎pkg/compose/umask_test.go‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121

2222
"github.com/compose-spec/compose-go/v2/types"
2323
"gotest.tools/v3/assert"
24+
25+
"github.com/docker/compose/v5/internal/confighash"
2426
)
2527

2628
func TestUmaskOption(t *testing.T) {
@@ -87,7 +89,7 @@ func TestGetCreateConfigsUmask(t *testing.T) {
8789
func TestServiceHashTracksUmask(t *testing.T) {
8890
zero, mask := uint32(0), uint32(0o77)
8991
hash := func(umask *uint32) string {
90-
h, err := ServiceHash(types.ServiceConfig{Name: "app", ContainerSpec: types.ContainerSpec{Image: "alpine", Umask: umask}})
92+
h, err := confighash.ServiceHash(types.ServiceConfig{Name: "app", ContainerSpec: types.ContainerSpec{Image: "alpine", Umask: umask}})
9193
assert.NilError(t, err)
9294
return h
9395
}

0 commit comments

Comments
 (0)